pangweixin 5 rokov pred
rodič
commit
73769116ec
57 zmenil súbory, kde vykonal 4423 pridanie a 156 odobranie
  1. 5 4
      app/build.gradle
  2. 0 26
      app/src/androidTest/java/com/qingdaofushan/homecaresas/ExampleInstrumentedTest.java
  3. 9 6
      app/src/main/AndroidManifest.xml
  4. 220 0
      app/src/main/java/com/qingdaofushan/homecaresas/activity/CamActivity.java
  5. 33 6
      app/src/main/java/com/qingdaofushan/homecaresas/activity/LiveActivity.java
  6. 132 25
      app/src/main/java/com/qingdaofushan/homecaresas/activity/MainActivity.java
  7. 1 1
      app/src/main/java/com/qingdaofushan/homecaresas/bean/AppUpDataItem.java
  8. 61 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/ExpandsBean.java
  9. 217 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/FetchUpBean.java
  10. 50 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/ItemCommon.java
  11. 53 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/ItemCommonList.java
  12. 4 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/UserInfo.java
  13. 95 0
      app/src/main/java/com/qingdaofushan/homecaresas/bean/appVMBean.java
  14. 3 1
      app/src/main/java/com/qingdaofushan/homecaresas/component/ApiInterceptor.java
  15. 4 1
      app/src/main/java/com/qingdaofushan/homecaresas/component/App.java
  16. 1 0
      app/src/main/java/com/qingdaofushan/homecaresas/data/ConstantData.java
  17. 145 0
      app/src/main/java/com/qingdaofushan/homecaresas/data/JsonCallBack.java
  18. 4 4
      app/src/main/java/com/qingdaofushan/homecaresas/data/Url.java
  19. 0 4
      app/src/main/java/com/qingdaofushan/homecaresas/data/UserInfo.java
  20. 156 0
      app/src/main/java/com/qingdaofushan/homecaresas/utils/Uri2PathUtil.java
  21. 13 12
      app/src/main/java/com/qingdaofushan/homecaresas/view/VersionCheckingUtil.java
  22. 21 5
      app/src/main/java/com/qingdaofushan/homecaresas/weiview/AndroidInterface.java
  23. 192 36
      app/src/main/java/com/qingdaofushan/homecaresas/weiview/WebViewActivity.java
  24. 387 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/activity/CaptureActivity.java
  25. 48 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/AutoFocusCallback.java
  26. 278 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/CameraConfigurationManager.java
  27. 398 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/CameraManager.java
  28. 150 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/FlashlightManager.java
  29. 133 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/PlanarYUVLuminanceSource.java
  30. 59 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/PreviewCallback.java
  31. 139 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/CaptureActivityHandler.java
  32. 104 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeFormatManager.java
  33. 112 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeHandler.java
  34. 86 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeThread.java
  35. 47 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/FinishListener.java
  36. 71 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/InactivityTimer.java
  37. 190 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/Intents.java
  38. 104 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/RGBLuminanceSource.java
  39. 132 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/encoding/EncodingHandler.java
  40. 12 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/Constant.java
  41. 92 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/UriUtil.java
  42. 33 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/ViewfinderResultPointCallback.java
  43. 285 0
      app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/ViewfinderView.java
  44. BIN
      app/src/main/res/drawable/flash_off.png
  45. BIN
      app/src/main/res/drawable/flash_on.png
  46. 8 0
      app/src/main/res/layout/activity_cam.xml
  47. 39 4
      app/src/main/res/layout/activity_live.xml
  48. 44 0
      app/src/main/res/layout/activity_scanner.xml
  49. 0 2
      app/src/main/res/layout/layout_download_dialog.xml
  50. BIN
      app/src/main/res/mipmap-xhdpi/btn_end_call.png
  51. BIN
      app/src/main/res/mipmap-xhdpi/icon_zanting.png
  52. BIN
      app/src/main/res/raw/beep.ogg
  53. 24 0
      app/src/main/res/values-sw432dp/attrs.xml
  54. 19 0
      app/src/main/res/values-sw432dp/ids.xml
  55. 8 0
      app/src/main/res/values/colors.xml
  56. 0 17
      app/src/test/java/com/qingdaofushan/homecaresas/ExampleUnitTest.java
  57. 2 2
      libWSLive/build.gradle

+ 5 - 4
app/build.gradle

@@ -1,12 +1,12 @@
 apply plugin: 'com.android.application'
 apply plugin: 'com.yanzhenjie.andserver'
 android {
-    compileSdk 30
+    compileSdk 29
 
     defaultConfig {
         applicationId "com.qingdaofushan.homecaresas"
         minSdk 24
-        targetSdk 30
+        targetSdk 29
         versionCode 1
         versionName "1.0"
 
@@ -46,7 +46,6 @@ dependencies {
 
     // 权限处理框架
     implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
-
     implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
 
     // 解析数据 gson
@@ -56,5 +55,7 @@ dependencies {
     implementation 'com.lzy.net:okrx2:2.0.2'
     implementation 'com.lzy.net:okserver:2.0.5'
     implementation 'org.apache.commons:commons-compress:1.18'
-    implementation 'com.github.jenly1314:zxing-lite:2.1.1'
+//    implementation 'com.github.jenly1314:zxing-lite:2.1.1'
+    implementation 'com.github.kongzue.DialogX:DialogX:0.0.40'
+    implementation 'com.google.zxing:core:3.3.0'
 }

+ 0 - 26
app/src/androidTest/java/com/qingdaofushan/homecaresas/ExampleInstrumentedTest.java

@@ -1,26 +0,0 @@
-package com.qingdaofushan.homecaresas;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
-    @Test
-    public void useAppContext() {
-        // Context of the app under test.
-        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        assertEquals("com.qingdaofushan.homecaresas", appContext.getPackageName());
-    }
-}

+ 9 - 6
app/src/main/AndroidManifest.xml

@@ -6,7 +6,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.VIBRATE" />
 
     <application
         android:name=".component.App"
@@ -18,8 +18,7 @@
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/Theme.HomeCareSAS">
-        <activity
-            android:name=".activity.LiveActivity" />
+        <activity android:name=".activity.LiveActivity" />
         <activity android:name=".weiview.WebViewActivity" />
         <activity
             android:name=".activity.MainActivity"
@@ -30,9 +29,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity
-            android:name="com.king.zxing.CaptureActivity"
-            android:theme="@style/CaptureTheme"/>
+        <!--        <activity-->
+        <!--            android:name="com.king.zxing.CaptureActivity"-->
+        <!--            android:theme="@style/CaptureTheme"/>-->
+        <activity android:name=".activity.CamActivity">
+        </activity>
+        <activity android:name=".zxing.activity.CaptureActivity" />
+
         <service
             android:name=".component.CoreService"
             android:exported="false" /> <!-- Android 7.0 拍照要用这个 -->

+ 220 - 0
app/src/main/java/com/qingdaofushan/homecaresas/activity/CamActivity.java

@@ -0,0 +1,220 @@
+package com.qingdaofushan.homecaresas.activity;
+
+import android.Manifest;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.icu.text.SimpleDateFormat;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.os.EnvironmentCompat;
+
+import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.utils.Uri2PathUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class CamActivity extends AppCompatActivity {
+
+//    private ImageView ivCamera;
+//    private ImageView ivPhoto;
+
+    // 拍照的requestCode
+    private static final int CAMERA_REQUEST_CODE = 0x00000010;
+    // 申请相机权限的requestCode
+    private static final int PERMISSION_CAMERA_REQUEST_CODE = 0x00000012;
+    /**
+     * 用于保存拍照图片的uri
+     */
+    private Uri mCameraUri;
+
+    /**
+     * 用于保存图片的文件路径,Android 10以下使用图片路径访问图片
+     */
+    private String mCameraImagePath;
+
+    /**
+     * 是否是Android 10以上手机
+     */
+    private boolean isAndroidQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_cam);
+        checkPermissionAndCamera();
+    }
+
+    /**
+     * 检查权限并拍照。
+     * 调用相机前先检查权限。
+     */
+    private void checkPermissionAndCamera() {
+        int hasCameraPermission = ContextCompat.checkSelfPermission(getApplication(),
+                Manifest.permission.CAMERA);
+        if (hasCameraPermission == PackageManager.PERMISSION_GRANTED) {
+            //有权限,调起相机拍照。
+            openCamera();
+        } else {
+            //没有权限,申请权限。
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
+                    PERMISSION_CAMERA_REQUEST_CODE);
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                    PERMISSION_CAMERA_REQUEST_CODE);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        System.out.println("==================resultCode = " + resultCode);
+        if (requestCode == CAMERA_REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                String uploadpath = Uri2PathUtil.getRealPathFromUri(this, mCameraUri);
+                Log.e("pwx", "onActivityResult: " + uploadpath);
+
+                finish();
+//                ivPhoto.setImageURI(mCameraUri);
+            } else {
+                Toast.makeText(this, "取消", Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+
+    /**
+     * 处理权限申请的回调。
+     *
+     * @param requestCode
+     * @param permissions
+     * @param grantResults
+     */
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        if (requestCode == PERMISSION_CAMERA_REQUEST_CODE) {
+            if (grantResults.length > 0
+                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                //允许权限,有调起相机拍照。
+                openCamera();
+            } else {
+                //拒绝权限,弹出提示框。
+                Toast.makeText(this, "拍照权限被拒绝", Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+
+    /**
+     * 调起相机拍照
+     */
+    private void openCamera() {
+        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        // 判断是否有相机
+        if (captureIntent.resolveActivity(getPackageManager()) != null) {
+            File photoFile = null;
+            Uri photoUri = null;
+
+            if (isAndroidQ) {
+                // 适配android 10
+                photoUri = createImageUri();
+            } else {
+                try {
+                    photoFile = createImageFile();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+                if (photoFile != null) {
+                    mCameraImagePath = photoFile.getAbsolutePath();
+                    System.out.println("path = " + mCameraImagePath);
+                    photoUri = FileProvider.getUriForFile(CamActivity.this,
+                            "com.qingdaofushan.homecaresas.fileprovider", photoFile);
+                }
+            }
+
+            System.out.println("photoUri = " + photoUri);
+            mCameraUri = photoUri;
+            if (photoUri != null) {
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                    List<ResolveInfo> resInfoList = getPackageManager()
+                            .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
+                    for (ResolveInfo resolveInfo : resInfoList) {
+                        String packageName = resolveInfo.activityInfo.packageName;
+                        grantUriPermission(packageName, photoUri,
+                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                    }
+                }
+                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
+                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);
+            }
+        }
+    }
+
+    /**
+     * 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
+     *
+     * @return 图片的uri
+     */
+    private Uri createImageUri() {
+        //设置保存参数到ContentValues中
+        ContentValues contentValues = new ContentValues();
+        //设置文件名
+        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() + "");
+        //兼容Android Q和以下版本
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            //android Q中不再使用DATA字段,而用RELATIVE_PATH代替
+            //TODO RELATIVE_PATH是相对路径不是绝对路径;照片存储的地方为:内部存储/Pictures/preventpro
+            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/preventpro");
+        }
+        //设置文件类型
+        contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG");
+        //执行insert操作,向系统文件夹中添加文件
+        //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
+        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
+        return uri;
+
+    }
+
+    /**
+     * 创建保存图片的文件
+     *
+     * @return
+     * @throws IOException
+     */
+    private File createImageFile() throws IOException {
+        String imageName = new SimpleDateFormat("yyyyMMdd_HHmmss",
+                Locale.getDefault()).format(new Date()) + ".jpg";
+//        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+//        File storageDir = Environment.getExternalStoragePublicDirectory(
+//                Environment.DIRECTORY_PICTURES);
+        File storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+                + File.separator + "Pictures" + File.separator + "abc");
+        if (!storageDir.exists()) storageDir.mkdirs();
+
+        File tempFile = new File(storageDir, imageName);
+        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
+            return null;
+        }
+        return tempFile;
+    }
+
+
+}

+ 33 - 6
app/src/main/java/com/qingdaofushan/homecaresas/activity/LiveActivity.java

@@ -5,6 +5,8 @@ import androidx.appcompat.app.AppCompatActivity;
 import android.Manifest;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.view.View;
+import android.widget.RelativeLayout;
 
 import com.qingdaofushan.homecaresas.event.EventData;
 import com.qingdaofushan.homecaresas.R;
@@ -18,18 +20,21 @@ import me.lake.librestreaming.core.listener.RESConnectionListener;
 import me.lake.librestreaming.ws.StreamAVOption;
 import me.lake.librestreaming.ws.StreamLiveCameraView;
 
-public class LiveActivity extends AppCompatActivity {
+public class LiveActivity extends AppCompatActivity implements View.OnClickListener {
 
     private StreamLiveCameraView mLiveCameraView;
     private StreamAVOption streamAVOption;
     private String rtmpUrl = "rtmp://blsspush.qingdaofushan.com/fushan/888888";
+    private RelativeLayout rl_suspend;
+    private RelativeLayout rl_stop;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_live);
-        rtmpUrl=getIntent().getStringExtra("url");
-        if(TextUtils.isEmpty(rtmpUrl)){
+        initView();
+        rtmpUrl = getIntent().getStringExtra("url");
+        if (TextUtils.isEmpty(rtmpUrl)) {
             ToastUtil.showToast("推流地址有误");
             finish();
             return;
@@ -50,16 +55,36 @@ public class LiveActivity extends AppCompatActivity {
         if (!mLiveCameraView.isStreaming()) {
             mLiveCameraView.startStreaming(rtmpUrl);
         }
-
         findViewById(R.id.tv_stop).setOnClickListener(view -> {
+
+        });
+
+    }
+
+    private void initView() {
+        rl_suspend = findViewById(R.id.rl_suspend);
+        rl_stop = findViewById(R.id.rl_stop);
+        rl_suspend.setOnClickListener(this);
+        rl_stop.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (R.id.rl_suspend == v.getId()) {
+            //暂停
+            if (mLiveCameraView.isStreaming()) {
+                mLiveCameraView.stopStreaming();
+            }
+            EventBus.getDefault().post(new EventData(2));
+            finish();
+        } else if (R.id.rl_stop == v.getId()) {
             //停止
             if (mLiveCameraView.isStreaming()) {
                 mLiveCameraView.stopStreaming();
             }
             EventBus.getDefault().post(new EventData(1));
             finish();
-        });
-
+        }
     }
 
     /**
@@ -101,4 +126,6 @@ public class LiveActivity extends AppCompatActivity {
         super.onDestroy();
         mLiveCameraView.destroy();
     }
+
+
 }

+ 132 - 25
app/src/main/java/com/qingdaofushan/homecaresas/activity/MainActivity.java

@@ -1,30 +1,53 @@
 package com.qingdaofushan.homecaresas.activity;
 
+import static com.lzy.okgo.utils.HttpUtils.runOnUiThread;
+
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 
 import android.Manifest;
+import android.content.ContentResolver;
 import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
+import android.provider.MediaStore;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.widget.EditText;
 import android.widget.Toast;
 
+import com.google.gson.Gson;
+import com.kongzue.dialogx.dialogs.WaitDialog;
 import com.lzy.okgo.OkGo;
 import com.lzy.okgo.model.Progress;
+import com.lzy.okgo.model.Response;
 import com.lzy.okgo.request.GetRequest;
 import com.lzy.okserver.OkDownload;
 import com.lzy.okserver.download.DownloadListener;
 import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.bean.ExpandsBean;
+import com.qingdaofushan.homecaresas.bean.FetchUpBean;
+import com.qingdaofushan.homecaresas.bean.ItemCommonList;
+import com.qingdaofushan.homecaresas.bean.appVMBean;
 import com.qingdaofushan.homecaresas.component.ServerManager;
+import com.qingdaofushan.homecaresas.data.ConstantData;
+import com.qingdaofushan.homecaresas.data.JsonCallBack;
+import com.qingdaofushan.homecaresas.data.Url;
 import com.qingdaofushan.homecaresas.utils.CompressUtil;
 import com.qingdaofushan.homecaresas.utils.ToastUtil;
+import com.qingdaofushan.homecaresas.view.VersionCheckingUtil;
 import com.qingdaofushan.homecaresas.weiview.WebViewActivity;
 import com.tbruyelle.rxpermissions2.RxPermissions;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -37,13 +60,27 @@ public class MainActivity extends AppCompatActivity {
 
     private String mRootUrl;
     private ServerManager mServerManager;
-    //    private Server mServer;
 
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
+        initPermission();
+        EditText editText = findViewById(R.id.ed_text);
+        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mServerManager.startServer();
+                Intent intent = new Intent(MainActivity.this, WebViewActivity.class);
+//                intent.putExtra("url", editText.getText().toString());
+                intent.putExtra("url", "http://127.0.0.1:8080");
+                startActivity(intent);
+            }
+        });
+    }
+
+    private void initPermission() {
         Observable<Boolean> request = new RxPermissions(this).request(
                 Manifest.permission.READ_EXTERNAL_STORAGE,
                 Manifest.permission.CAMERA,
@@ -51,60 +88,57 @@ public class MainActivity extends AppCompatActivity {
         );
         request.subscribe(aBoolean -> {
             if (aBoolean) {
-
+                // 初始化HTTP服务
+                mServerManager = new ServerManager(this);
+                mServerManager.register();
+                getVM();
             } else {
                 ToastUtil.showToast("权限被拒绝");
             }
         });
-        initApp();
-        // 初始化HTTP服务
-        mServerManager = new ServerManager(this);
-        mServerManager.register();
-        EditText editText = findViewById(R.id.ed_text);
-        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mServerManager.startServer();
-                Intent intent = new Intent(MainActivity.this, WebViewActivity.class);
-                intent.putExtra("url","http://127.0.0.1:8080");
-                startActivity(intent);
-            }
-        });
 
     }
 
-    private void initApp() {
+    private void initApp(String appWebUrl, int updataType) {
         OkDownload.getInstance().setFolder(Environment.getExternalStorageDirectory().getAbsolutePath() + "/homeLife/");
         File indexFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/homeLife/homeLifemini/index.html");
         File zipFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/homeLife/homeLifemini.zip");
-        if (!indexFile.exists()) {
+        if (!indexFile.exists() || updataType == 1) {
             if (zipFile.exists()) {
                 zipFile.delete();
             }
-            String url = "https://pwxmax.cn/homeLifemini.zip";
-            GetRequest<File> requesta = OkGo.get(url);
-            OkDownload.request(url, requesta)//
+            WaitDialog.show("正在进行第一次加载...", 0f);
+//            String url = "https://pwxmax.cn/homeLifemini.zip";
+            GetRequest<File> requesta = OkGo.get(appWebUrl);
+            OkDownload.request(appWebUrl, requesta)//
                     .save()
-                    .register(new DownloadListener(url) {
+                    .register(new DownloadListener(appWebUrl) {
                         @Override
                         public void onStart(Progress progress) {
-                            ToastUtil.showToast("开始");
+                            ToastUtil.showToast("开始下载");
                         }
 
                         @Override
                         public void onProgress(Progress progress) {
-
+                            WaitDialog.show("正在进行第一次加载...", progress.fraction);
                         }
 
                         @Override
                         public void onError(Progress progress) {
                             ToastUtil.showToast("错误");
+                            Log.e("pwx", "onError: " + progress.exception.toString());
+                            WaitDialog.dismiss();
                         }
 
                         @Override
                         public void onFinish(File file, Progress progress) {
                             CompressUtil.unzip(file, Environment.getExternalStorageDirectory().getAbsolutePath() + "/homeLife/");
-                            ToastUtil.showToast("完成" + file.getAbsolutePath());
+                            ToastUtil.showToast("下载完成");
+                            mServerManager.startServer();
+                            Intent intent = new Intent(MainActivity.this, WebViewActivity.class);
+                            intent.putExtra("url", "http://127.0.0.1:8080");
+                            startActivity(intent);
+                            WaitDialog.dismiss();
                         }
 
                         @Override
@@ -115,6 +149,57 @@ public class MainActivity extends AppCompatActivity {
         }
     }
 
+    private void getVM() {
+        File versionFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/homeLife/homeLifemini/version");
+        FetchUpBean fetchUpBean = new FetchUpBean();
+        ArrayList<FetchUpBean.QuerysBean> querysBeans = new ArrayList<>();
+        FetchUpBean.QuerysBean querysBean = new FetchUpBean.QuerysBean();
+        FetchUpBean.QuerysBean.OrderbyBean orderbyBean = new FetchUpBean.QuerysBean.OrderbyBean();
+        ArrayList<String> strings = new ArrayList<>();
+        strings.add("createTime");
+        orderbyBean.setColumns(strings);
+        orderbyBean.setSeq("DESC");
+        querysBean.setOrderby(orderbyBean);
+        FetchUpBean.QuerysBean.LimitBean limitBean = new FetchUpBean.QuerysBean.LimitBean();
+        limitBean.setStart(0);
+        limitBean.setLength(1);
+        querysBean.setLimit(limitBean);
+        querysBean.setOrderby(orderbyBean);
+        querysBeans.add(querysBean);
+        fetchUpBean.setQuerys(querysBeans);
+        OkGo.<ItemCommonList<appVMBean, ExpandsBean>>post(Url.URL_VERSION)
+                .upJson(new Gson().toJson(fetchUpBean))
+                .execute(new JsonCallBack<ItemCommonList<appVMBean, ExpandsBean>>() {
+                    @Override
+                    public void onSuccess(Response<ItemCommonList<appVMBean, ExpandsBean>> response) {
+                        super.onSuccess(response);
+                        ItemCommonList<appVMBean, ExpandsBean> body = response.body();
+                        if (body.getCode() == ConstantData.CODE_RESULT_SUCCESS) {
+                            if (body.getData() != null && body.getData().size() > 0) {
+                                if (body.getData().get(0).getUpdateType() == 2) {
+                                    new VersionCheckingUtil().isChecking(MainActivity.this, body.getData().get(0), 0);
+                                } else if (body.getData().get(0).getUpdateType() == 1) {
+                                    if (versionFile.exists()) {
+                                        if (!readFileContent(versionFile).equals(body.getData().get(0).getWebVersion())) {
+                                            initApp(body.getData().get(0).getAppWebUrl(), 1);
+                                        }
+                                    } else {
+                                        initApp(body.getData().get(0).getAppWebUrl(), 1);
+                                    }
+                                }
+                            }
+                        } else {
+                            ToastUtil.showToast("加载失败");
+                        }
+                    }
+
+                    @Override
+                    public void onError(Response<ItemCommonList<appVMBean, ExpandsBean>> response) {
+                        super.onError(response);
+                    }
+                });
+    }
+
     /**
      * Start notify.
      */
@@ -129,6 +214,28 @@ public class MainActivity extends AppCompatActivity {
         }
     }
 
+    /**
+     * 【读取文件内容】
+     **/
+    public static String readFileContent(File file) {
+        try {
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                byte[] buffer = new byte[32 * 1024];
+                FileInputStream fis = new FileInputStream(file);
+                int len = 0;
+                StringBuffer sb = new StringBuffer("");
+                while ((len = fis.read(buffer)) > 0) {
+                    sb.append(new String(buffer, 0, len));
+                }
+                fis.close();
+                return sb.toString();
+            }
+        } catch (Exception e) {
+            return "";
+        }
+        return null;
+    }
+
     /**
      * Error notify.
      */

+ 1 - 1
app/src/main/java/com/qingdaofushan/homecaresas/data/AppUpDataItem.java → app/src/main/java/com/qingdaofushan/homecaresas/bean/AppUpDataItem.java

@@ -1,4 +1,4 @@
-package com.qingdaofushan.homecaresas.data;
+package com.qingdaofushan.homecaresas.bean;
 
 import java.io.Serializable;
 

+ 61 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/ExpandsBean.java

@@ -0,0 +1,61 @@
+package com.qingdaofushan.homecaresas.bean;
+
+public class ExpandsBean {
+
+    /**
+     * total : 4
+     * start : 0
+     * length : 30
+     */
+
+    private String total;
+    private String start;
+    private String length;
+    /**
+     * authToken : 800A4aBa68604A9B99372102Fc0798F5
+     * accessToken : EC557346af8b440780cB98DaA9b55F4D293D0631102F40B4bff3013d643DE694
+     */
+
+    private String authToken;
+    private String accessToken;
+
+    public String getTotal() {
+        return total;
+    }
+
+    public void setTotal(String total) {
+        this.total = total;
+    }
+
+    public String getStart() {
+        return start;
+    }
+
+    public void setStart(String start) {
+        this.start = start;
+    }
+
+    public String getLength() {
+        return length;
+    }
+
+    public void setLength(String length) {
+        this.length = length;
+    }
+
+    public String getAuthToken() {
+        return authToken;
+    }
+
+    public void setAuthToken(String authToken) {
+        this.authToken = authToken;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+}

+ 217 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/FetchUpBean.java

@@ -0,0 +1,217 @@
+package com.qingdaofushan.homecaresas.bean;
+
+import java.util.List;
+
+public class FetchUpBean {
+
+    /**
+     * querys : [{"orderby":{"columns":["createTime"],"seq":"DESC"},"limit":{"start":0,"length":30}}]
+     * expands : {"options":"all"}
+     */
+
+    private ExpandsBean expands;
+    private List<QuerysBean> querys;
+
+    public ExpandsBean getExpands() {
+        return expands;
+    }
+
+    public void setExpands(ExpandsBean expands) {
+        this.expands = expands;
+    }
+
+    public List<QuerysBean> getQuerys() {
+        return querys;
+    }
+
+    public void setQuerys(List<QuerysBean> querys) {
+        this.querys = querys;
+    }
+
+    public static class ExpandsBean {
+        /**
+         * options : all
+         */
+
+        private String options;
+        private String search;
+        private int start;
+        private int length;
+
+        public int getStart() {
+            return start;
+        }
+
+        public void setStart(int start) {
+            this.start = start;
+        }
+
+        public int getLength() {
+            return length;
+        }
+
+        public void setLength(int length) {
+            this.length = length;
+        }
+
+        public String getSearch() {
+            return search;
+        }
+
+        public void setSearch(String search) {
+            this.search = search;
+        }
+
+        public String getOptions() {
+            return options;
+        }
+
+        public void setOptions(String options) {
+            this.options = options;
+        }
+    }
+
+    public static class QuerysBean {
+        /**
+         * orderby : {"columns":["createTime"],"seq":"DESC"}
+         * limit : {"start":0,"length":30}
+         * condition : {"symbol":"LT","column":"updateTime","value":"2020-11-05T20:43:52Z"}
+         */
+
+        private OrderbyBean orderby;
+        private LimitBean limit;
+        private ConditionBean condition;
+        private String phoneNumber;
+        private String id;
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getPhoneNumber() {
+            return phoneNumber;
+        }
+
+        public void setPhoneNumber(String phoneNumber) {
+            this.phoneNumber = phoneNumber;
+        }
+
+        public OrderbyBean getOrderby() {
+            return orderby;
+        }
+
+        public void setOrderby(OrderbyBean orderby) {
+            this.orderby = orderby;
+        }
+
+        public LimitBean getLimit() {
+            return limit;
+        }
+
+        public void setLimit(LimitBean limit) {
+            this.limit = limit;
+        }
+
+        public ConditionBean getCondition() {
+            return condition;
+        }
+
+        public void setCondition(ConditionBean condition) {
+            this.condition = condition;
+        }
+
+        public static class OrderbyBean {
+            /**
+             * columns : ["createTime"]
+             * seq : DESC
+             */
+
+            private String seq;
+            private List<String> columns;
+
+            public String getSeq() {
+                return seq;
+            }
+
+            public void setSeq(String seq) {
+                this.seq = seq;
+            }
+
+            public List<String> getColumns() {
+                return columns;
+            }
+
+            public void setColumns(List<String> columns) {
+                this.columns = columns;
+            }
+        }
+
+        public static class LimitBean {
+            /**
+             * start : 0
+             * length : 30
+             */
+
+            private int start;
+            private int length;
+
+            public int getStart() {
+                return start;
+            }
+
+            public void setStart(int start) {
+                this.start = start;
+            }
+
+            public int getLength() {
+                return length;
+            }
+
+            public void setLength(int length) {
+                this.length = length;
+            }
+        }
+
+        public static class ConditionBean {
+            /**
+             * symbol : LT
+             * column : updateTime
+             * value : 2020-11-05T20:43:52Z
+             */
+
+            private String symbol;
+            private String column;
+            private String value;
+
+            public String getSymbol() {
+                return symbol;
+            }
+
+            public void setSymbol(String symbol) {
+                this.symbol = symbol;
+            }
+
+            public String getColumn() {
+                return column;
+            }
+
+            public void setColumn(String column) {
+                this.column = column;
+            }
+
+            public String getValue() {
+                return value;
+            }
+
+            public void setValue(String value) {
+                this.value = value;
+            }
+        }
+    }
+
+
+}

+ 50 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/ItemCommon.java

@@ -0,0 +1,50 @@
+package com.qingdaofushan.homecaresas.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+public class ItemCommon<T, V> {
+
+    @SerializedName("code")
+    private int code;
+
+    @SerializedName("message")
+    private String msg;
+
+    @SerializedName("datas")
+    private T data;
+    @SerializedName("expands")
+    private V expands;
+
+    public V getExpands() {
+        return expands;
+    }
+
+    public void setExpands(V expands) {
+        this.expands = expands;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+}

+ 53 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/ItemCommonList.java

@@ -0,0 +1,53 @@
+package com.qingdaofushan.homecaresas.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class ItemCommonList<T, V> implements Serializable {
+
+    @SerializedName("code")
+    private int code;
+
+    @SerializedName("message")
+    private String msg;
+
+    @SerializedName("datas")
+    private List<T> data;
+
+    @SerializedName("expands")
+    private V expands;
+
+    public V getExpands() {
+        return expands;
+    }
+
+    public void setExpands(V expands) {
+        this.expands = expands;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public List<T> getData() {
+        return data;
+    }
+
+    public void setData(List<T> data) {
+        this.data = data;
+    }
+}

+ 4 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/UserInfo.java

@@ -0,0 +1,4 @@
+package com.qingdaofushan.homecaresas.bean;
+
+public class UserInfo {
+}

+ 95 - 0
app/src/main/java/com/qingdaofushan/homecaresas/bean/appVMBean.java

@@ -0,0 +1,95 @@
+package com.qingdaofushan.homecaresas.bean;
+
+public class appVMBean {
+
+    private String appApkUrl;
+    private String appWebUrl;
+    private String createTime;
+    private String id;
+    private Integer updateLevel;
+    private String updateTime;
+    private Integer updateType;
+    private String version;
+    private String versionDoc;
+    private String webVersion;
+
+    public String getWebVersion() {
+        return webVersion;
+    }
+
+    public void setWebVersion(String webVersion) {
+        this.webVersion = webVersion;
+    }
+
+    public String getAppApkUrl() {
+        return appApkUrl;
+    }
+
+    public void setAppApkUrl(String appApkUrl) {
+        this.appApkUrl = appApkUrl;
+    }
+
+    public String getAppWebUrl() {
+        return appWebUrl;
+    }
+
+    public void setAppWebUrl(String appWebUrl) {
+        this.appWebUrl = appWebUrl;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getUpdateLevel() {
+        return updateLevel;
+    }
+
+    public void setUpdateLevel(Integer updateLevel) {
+        this.updateLevel = updateLevel;
+    }
+
+    public String getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Integer getUpdateType() {
+        return updateType;
+    }
+
+    public void setUpdateType(Integer updateType) {
+        this.updateType = updateType;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getVersionDoc() {
+        return versionDoc;
+    }
+
+    public void setVersionDoc(String versionDoc) {
+        this.versionDoc = versionDoc;
+    }
+}

+ 3 - 1
app/src/main/java/com/qingdaofushan/homecaresas/component/ApiInterceptor.java

@@ -66,7 +66,9 @@ public class ApiInterceptor implements HandlerInterceptor {
 
     static {
         PROXY_PREFIXS = new HashMap<>();
-        PROXY_PREFIXS.put("/api", HttpHost.create("https://funingyuan.dev.qingdaofushan.com"));
+        PROXY_PREFIXS.put("/api", HttpHost.create("http://172.20.10.9:8080"));
+        PROXY_PREFIXS.put("/api/v1", HttpHost.create("https://funingyuan.dev.qingdaofushan.com"));
+        PROXY_PREFIXS.put("/api/store", HttpHost.create("https://funingyuan.dev.qingdaofushan.com"));
     }
 
     private final static Set<String> HOP_BY_HOP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(

+ 4 - 1
app/src/main/java/com/qingdaofushan/homecaresas/component/App.java

@@ -21,6 +21,7 @@ import android.os.Environment;
 
 import androidx.annotation.NonNull;
 
+import com.kongzue.dialogx.DialogX;
 import com.lzy.okgo.OkGo;
 import com.lzy.okgo.cache.CacheEntity;
 import com.lzy.okgo.cache.CacheMode;
@@ -47,9 +48,11 @@ public class App extends Application {
         super.onCreate();
         if (mInstance == null) {
             mInstance = this;
-//            initRootPath(this);
+            initRootPath(this);
         }
         initOkGo();
+        //初始化弹窗
+        DialogX.init(this);
     }
 
     @NonNull

+ 1 - 0
app/src/main/java/com/qingdaofushan/homecaresas/data/ConstantData.java

@@ -3,6 +3,7 @@ package com.qingdaofushan.homecaresas.data;
 
 import android.os.Environment;
 
+import com.qingdaofushan.homecaresas.bean.UserInfo;
 
 
 /**

+ 145 - 0
app/src/main/java/com/qingdaofushan/homecaresas/data/JsonCallBack.java

@@ -0,0 +1,145 @@
+package com.qingdaofushan.homecaresas.data;
+
+
+import com.google.gson.Gson;
+import com.google.gson.stream.JsonReader;
+import com.lzy.okgo.OkGo;
+import com.lzy.okgo.callback.AbsCallback;
+import com.lzy.okgo.model.HttpHeaders;
+import com.lzy.okgo.request.base.Request;
+import com.qingdaofushan.homecaresas.bean.ItemCommon;
+import com.qingdaofushan.homecaresas.bean.ItemCommonList;
+import com.qingdaofushan.homecaresas.bean.UserInfo;
+import com.qingdaofushan.homecaresas.utils.ToastUtil;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.ConnectException;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Map;
+
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+public abstract class JsonCallBack<T> extends AbsCallback<T> {
+
+    public JsonCallBack() {
+    }
+
+    @Override
+    public void onSuccess(com.lzy.okgo.model.Response<T> response) {
+        T body = response.body();
+        int status = 0;
+        if (body instanceof ItemCommon) {
+            status = ((ItemCommon) body).getCode();
+        } else if (body instanceof ItemCommonList) {
+            status = ((ItemCommonList) body).getCode();
+        }
+        if (status == 403) {
+            ToastUtil.showToast("没有访问权限");
+            return;
+        }
+        if (status == 414) {
+//            Map<String, String> headers = CuracaoAndroidSignature.auth(ConstantData.ACL_APP, ConstantData.ACL_SECRET, ConstantData.getUserInfo().getAccessToken());
+//            HttpHeaders httpHeaders = new HttpHeaders();
+//            for (Map.Entry<String, String> entry : headers.entrySet()) {
+//                httpHeaders.put(entry.getKey(), entry.getValue());
+//            }
+//            OkGo.<ItemCommonList<UserInfo, ExpandsBean>>post(Url.URL_GET_TOKEN)
+//                    .headers(httpHeaders)
+//                    .execute(new AbsCallback<ItemCommonList<UserInfo, ExpandsBean>>() {
+//                        @Override
+//                        public ItemCommonList<UserInfo, ExpandsBean> convertResponse(Response response) {
+//                            ResponseBody body = response.body();
+//                            if (null == body) return null;
+//                            ItemCommonList<UserInfo, ExpandsBean> data = null;
+//                            Gson gson = new Gson();
+//                            JsonReader jsonReader = new JsonReader(body.charStream());
+//                            Type genType = getClass().getGenericSuperclass();
+//                            Type type = ((ParameterizedType) genType).getActualTypeArguments()[0];
+//                            data = gson.fromJson(jsonReader, type);
+//                            return data;
+//                        }
+//
+//                        @Override
+//                        public void onSuccess(com.lzy.okgo.model.Response<ItemCommonList<UserInfo, ExpandsBean>> response) {
+//                            ItemCommonList<UserInfo, ExpandsBean> body = response.body();
+//                            if (body.getCode() == ConstantData.CODE_RESULT_SUCCESS) {
+//                                List<UserInfo> data = body.getData();
+//                                if (data != null) {
+//                                    UserInfo userInfo = data.get(0);
+//                                    userInfo.setAccessToken(body.getExpands().getAccessToken());
+//                                    userInfo.setAuthToken(body.getExpands().getAuthToken());
+//                                    ConstantData.setToken(body.getExpands().getAuthToken());
+//                                    ConstantData.setUserInfo(userInfo);
+//                                    ConstantData.setIsLogin(true);
+//                                    SharedPreferenceUtil.put(
+//                                            BaseApplication.context,
+//                                            "UserInfo",
+//                                            new Gson().toJson(data.get(0)));
+//                                    SharedPreferenceUtil.put(BaseApplication.context, "userId", data.get(0).getId());
+//                                }
+//                            }
+//                        }
+//
+//                        @Override
+//                        public void onError(com.lzy.okgo.model.Response<ItemCommonList<UserInfo, ExpandsBean>> response) {
+//                            super.onError(response);
+//
+//                        }
+//                    });
+//            LoginTokenUtils.logOutForNoneToken(status);
+        }
+    }
+
+    @Override
+    public void onStart(Request<T, ? extends Request> request) {
+        super.onStart(request);
+
+        HttpHeaders httpHeaders = new HttpHeaders();
+        //TODO 添加请求token
+//        httpHeaders.put("Authorization", ConstantData.getUserInfo().getAuthToken());
+        request.headers(httpHeaders);
+        // 主要用于在所有请求之前添加公共的请求头或请求参数
+        // 例如登录授权的 token
+        // 使用的设备信息
+        // 可以随意添加,也可以什么都不传
+        // 还可以在这里对所有的参数进行加密,均在这里实现
+//        request.headers("Content-Type", "application/json")//
+//                .headers("qytech-channel", "android-001")
+//                .headers("qytech-mac-address", Base64.encode(StringUtil.getMacAddr().getBytes()))
+//                .headers("qytech-product", "qianyue");
+    }
+
+    /**
+     * 该方法是子线程处理,不能做ui相关的工作
+     * 主要作用是解析网络返回的 response 对象,生产onSuccess回调中需要的数据对象
+     * 这里的解析工作不同的业务逻辑基本都不一样,所以需要自己实现,以下给出的时模板代码,实际使用根据需要修改
+     */
+    @Override
+    public T convertResponse(Response response) {
+        ResponseBody body = response.body();
+        if (null == body) return null;
+
+        T data = null;
+        Gson gson = new Gson();
+        JsonReader jsonReader = new JsonReader(body.charStream());
+        Type genType = getClass().getGenericSuperclass();
+        Type type = ((ParameterizedType) genType).getActualTypeArguments()[0];
+        data = gson.fromJson(jsonReader, type);
+        return data;
+    }
+
+    @Override
+    public void onError(com.lzy.okgo.model.Response<T> response) {
+//        super.onError(response);
+        ToastUtil.showToast("网络不给力~");
+        Throwable exception = response.getException();
+        if (exception != null) exception.printStackTrace();
+
+        if (exception instanceof UnknownHostException || exception instanceof ConnectException) {
+
+        }
+    }
+}

+ 4 - 4
app/src/main/java/com/qingdaofushan/homecaresas/data/Url.java

@@ -7,10 +7,10 @@ package com.qingdaofushan.homecaresas.data;
  */
 public class Url {
 
-    private static boolean isOutTest = false;
+    private static boolean isOutTest = true;
 
-    private static String baseInnerUrl = "";
-    private static final String baseOutUrl = "https://www.qianyue.online/api/v1/";
+    private static String baseInnerUrl = "http://192.168.1.6:8080/api/hlmini/";
+    private static final String baseOutUrl = "http://192.168.1.6:8080/api/hlmini/";
 //    public static final String IMGUrl = GlobalConstant.URL.replace("api/", "");
     private static String baseUrl;
 
@@ -28,7 +28,7 @@ public class Url {
 
     //========= app版本接口类 Version Controller =========//
 
-    public static final String URL_VERSION = baseUrl + "blade-customer-archives/androidversionrepository/getVersionInformation";
+    public static final String URL_VERSION = baseUrl + "appvm/fetch";
 
     //========= Circle Async Controller =========//
 

+ 0 - 4
app/src/main/java/com/qingdaofushan/homecaresas/data/UserInfo.java

@@ -1,4 +0,0 @@
-package com.qingdaofushan.homecaresas.data;
-
-public class UserInfo {
-}

+ 156 - 0
app/src/main/java/com/qingdaofushan/homecaresas/utils/Uri2PathUtil.java

@@ -0,0 +1,156 @@
+package com.qingdaofushan.homecaresas.utils;
+import android.annotation.TargetApi;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+import androidx.loader.content.CursorLoader;
+
+public class Uri2PathUtil {
+    //复杂版处理  (适配多种API)
+    public static String getRealPathFromUri(Context context, Uri uri) {
+        int sdkVersion = Build.VERSION.SDK_INT;
+        if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri);
+        if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri);
+        else return getRealPathFromUri_AboveApi19(context, uri);
+    }
+
+    /**
+     * 适配api19以上,根据uri获取图片的绝对路径
+     */
+    @TargetApi(Build.VERSION_CODES.KITKAT)
+    private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
+        if (DocumentsContract.isDocumentUri(context, uri)) {
+            if (isExternalStorageDocument(uri)) {
+                final String docId = DocumentsContract.getDocumentId(uri);
+                final String[] split = docId.split(":");
+                final String type = split[0];
+                if ("primary".equalsIgnoreCase(type)) {
+                    return Environment.getExternalStorageDirectory() + "/" + split[1];
+                }
+            } else if (isDownloadsDocument(uri)) {
+                final String id = DocumentsContract.getDocumentId(uri);
+                final Uri contentUri = ContentUris.withAppendedId(
+                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+                return getDataColumn(context, contentUri, null, null);
+            } else if (isMediaDocument(uri)) {
+                final String docId = DocumentsContract.getDocumentId(uri);
+                final String[] split = docId.split(":");
+                final String type = split[0];
+
+                Uri contentUri;
+                if ("image".equals(type)) {
+                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+                } else if ("video".equals(type)) {
+                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+                } else if ("audio".equals(type)) {
+                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+                } else {
+                    contentUri = MediaStore.Files.getContentUri("external");
+                }
+
+                final String selection = "_id=?";
+                final String[] selectionArgs = new String[]{split[1]};
+                return getDataColumn(context, contentUri, selection, selectionArgs);
+            }
+        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
+            return getDataColumn(context, uri, null, null);
+        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
+            return uri.getPath();
+        }
+
+        return null;
+    }
+
+    /**
+     * 适配api11-api18,根据uri获取图片的绝对路径
+     */
+    private static String getRealPathFromUri_Api11To18(Context context, Uri uri) {
+        String filePath = null;
+        String[] projection = {MediaStore.Images.Media.DATA};
+        //这个有两个包不知道是哪个。。。。不过这个复杂版一般用不到
+        CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null);
+        Cursor cursor = loader.loadInBackground();
+
+        if (cursor != null) {
+            cursor.moveToFirst();
+            filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+            cursor.close();
+        }
+        return filePath;
+    }
+
+    /**
+     * 适配api11以下(不包括api11),根据uri获取图片的绝对路径
+     */
+    private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) {
+        String filePath = null;
+        String[] projection = {MediaStore.Images.Media.DATA};
+        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+        if (cursor != null && cursor.moveToFirst()) {
+            filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+            cursor.close();
+        }
+        return filePath;
+    }
+
+    /**
+     * Get the value of the data column for this Uri. This is useful for
+     * MediaStore Uris, and other file-based ContentProviders.
+     *
+     * @param context       The context.
+     * @param uri           The Uri to query.
+     * @param selection     (Optional) Filter used in the query.
+     * @param selectionArgs (Optional) Selection arguments used in the query.
+     * @return The value of the _data column, which is typically a file path.
+     */
+    public static String getDataColumn(Context context, Uri uri, String selection,
+                                       String[] selectionArgs) {
+        Cursor cursor = null;
+        String column = MediaStore.MediaColumns.DATA;
+        String[] projection = {column};
+        try {
+            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+                    null);
+            if (cursor != null && cursor.moveToFirst()) {
+                int column_index = cursor.getColumnIndexOrThrow(column);
+                return cursor.getString(column_index);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+        return null;
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is ExternalStorageProvider.
+     */
+    public static boolean isExternalStorageDocument(Uri uri) {
+        return "com.android.externalstorage.documents".equals(uri.getAuthority());
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is DownloadsProvider.
+     */
+    public static boolean isDownloadsDocument(Uri uri) {
+        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is MediaProvider.
+     */
+    public static boolean isMediaDocument(Uri uri) {
+        return "com.android.providers.media.documents".equals(uri.getAuthority());
+    }
+}

+ 13 - 12
app/src/main/java/com/qingdaofushan/homecaresas/view/VersionCheckingUtil.java

@@ -26,8 +26,9 @@ import com.lzy.okserver.download.DownloadListener;
 import com.lzy.okserver.download.DownloadTask;
 import com.qingdaofushan.homecaresas.R;
 import com.qingdaofushan.homecaresas.activity.MainActivity;
+import com.qingdaofushan.homecaresas.bean.appVMBean;
 import com.qingdaofushan.homecaresas.component.App;
-import com.qingdaofushan.homecaresas.data.AppUpDataItem;
+import com.qingdaofushan.homecaresas.bean.AppUpDataItem;
 import com.qingdaofushan.homecaresas.downloadmanager.DefaultRetryPolicy;
 import com.qingdaofushan.homecaresas.downloadmanager.DownloadRequest;
 import com.qingdaofushan.homecaresas.downloadmanager.DownloadStatusListener;
@@ -42,7 +43,7 @@ import java.io.File;
  */
 public class VersionCheckingUtil {
     private Context mContext;
-    private AppUpDataItem mAppUpDataItem;
+    private appVMBean appVMBean;
     private DownloadDialog downloadDialog;
     private long mkeyTime;
     private int mType;
@@ -55,17 +56,17 @@ public class VersionCheckingUtil {
 
     /**
      * @param context
-     * @param appUpDataItem
+     * @param appVMBean
      * @param type          判断哪个类唤起的
      */
-    public void isChecking(Context context, AppUpDataItem appUpDataItem, int type) {
+    public void isChecking(Context context, appVMBean appVMBean, int type) {
         mContext = context;
-        mAppUpDataItem = appUpDataItem;
+        this.appVMBean = appVMBean;
         mType = type;
 
         //getUpdatelevel 是否强制更新
-        if (!DeviceUtil.getVersionName(mContext).equals(appUpDataItem.getVersion())) {
-            if (1 == appUpDataItem.getUpdateLevel()) {
+        if (!DeviceUtil.getVersionName(mContext).equals(appVMBean.getVersion())) {
+            if (1 == appVMBean.getUpdateLevel()) {
                 myShowDialog(false);
             } else {
                 myShowDialog(true);
@@ -85,7 +86,7 @@ public class VersionCheckingUtil {
             downloadDialog = new DownloadDialog(mContext);
             downloadDialog.setTitle("测到新版本,是否安装?");
             downloadDialog.setIsDismiss(b);
-            downloadDialog.setMessage(mAppUpDataItem.getVersionDesc());
+            downloadDialog.setMessage(appVMBean.getVersionDoc());
             downloadDialog.setOnClickBottomListener(new DownloadDialog.OnClickBottomListener() {
                 @Override
                 public void onPositiveClick() {
@@ -119,7 +120,7 @@ public class VersionCheckingUtil {
         //这一步,系统会自动为你在沙盒中创建文件夹
         apkFile = new File(filepath, filename);
         thinDownloadManager = new ThinDownloadManager(DOWNLOAD_THREAD_POOL_SIZE);
-        Uri downloadUri = Uri.parse(mAppUpDataItem.getAppUrl());
+        Uri downloadUri = Uri.parse(appVMBean.getAppApkUrl());
         Uri destinationUri = Uri.fromFile(apkFile);
         DownloadRequest downloadRequest = new DownloadRequest(downloadUri)
                 .setRetryPolicy(new DefaultRetryPolicy())
@@ -158,8 +159,8 @@ public class VersionCheckingUtil {
 
     //下载方法
     private void downloadApk() {
-        GetRequest<File> request = OkGo.get(mAppUpDataItem.getAppUrl());
-        DownloadTask task = OkDownload.request(mAppUpDataItem.getVersion(), request).save().register(new DownloadListener(mAppUpDataItem.getVersion()) {
+        GetRequest<File> request = OkGo.get(appVMBean.getAppApkUrl());
+        DownloadTask task = OkDownload.request(appVMBean.getVersion(), request).save().register(new DownloadListener(appVMBean.getVersion()) {
             @Override
             public void onStart(Progress progress) {
 
@@ -224,7 +225,7 @@ public class VersionCheckingUtil {
 //            mkeyTime = System.currentTimeMillis();
 //            ToastUtils.show("再按一次退出程序");
 //        } else {
-//            Intent intent = new Intent("com.sharedtalent.openhr.mvp.base.BaseMvpActivity");
+//            Intent intent = new Intent("com.qingdaofushan.homecaresas.mvp.base.BaseMvpActivity");
 //            intent.putExtra("closeAll", 1);
 //            mContext.sendBroadcast(intent);//发送广播
 //            Activity activity = null;

+ 21 - 5
app/src/main/java/com/qingdaofushan/homecaresas/weiview/AndroidInterface.java

@@ -9,11 +9,13 @@ import android.os.Looper;
 import android.webkit.JavascriptInterface;
 
 import com.just.agentweb.AgentWeb;
-import com.king.zxing.CaptureActivity;
 import com.qingdaofushan.homecaresas.activity.LiveActivity;
+import com.qingdaofushan.homecaresas.event.EventData;
 import com.qingdaofushan.homecaresas.utils.SharedPreferenceUtil;
 import com.qingdaofushan.homecaresas.utils.ToastUtil;
+import com.qingdaofushan.homecaresas.zxing.activity.CaptureActivity;
 
+import org.greenrobot.eventbus.EventBus;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -65,20 +67,23 @@ public class AndroidInterface {
             @Override
             public void run() {
                 //跳转的默认扫码界面
-                activity.startActivityForResult(new Intent(activity, CaptureActivity.class),200);
+                Intent intent = new Intent(activity, CaptureActivity.class);
+                activity.startActivityForResult(intent, 200);
+//                activity.startActivityForResult(new Intent(activity, CaptureActivity.class),200);
             }
         });
     }
+
     @JavascriptInterface
     public void StartService(String json) {
         deliver.post(new Runnable() {
             @Override
             public void run() {
                 try {
-                    JSONObject jsonObject=new JSONObject(json);
-                    String url= jsonObject.getString("url");
+                    JSONObject jsonObject = new JSONObject(json);
+                    String url = jsonObject.getString("url");
                     Intent intent = new Intent(activity, LiveActivity.class);
-                    intent.putExtra("url",url);
+                    intent.putExtra("url", url);
                     activity.startActivity(intent);
                 } catch (JSONException e) {
                     ToastUtil.showToast("读取参数报错");
@@ -86,4 +91,15 @@ public class AndroidInterface {
             }
         });
     }
+
+    @JavascriptInterface
+    public void Camera(String a) {
+        deliver.post(new Runnable() {
+            @Override
+            public void run() {
+                //跳转的拍照
+                EventBus.getDefault().post(new EventData(3));
+            }
+        });
+    }
 }

+ 192 - 36
app/src/main/java/com/qingdaofushan/homecaresas/weiview/WebViewActivity.java

@@ -1,29 +1,53 @@
 package com.qingdaofushan.homecaresas.weiview;
 
+import android.Manifest;
+import android.content.ContentValues;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
+import android.icu.text.SimpleDateFormat;
+import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
 import android.widget.LinearLayout;
+import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.os.EnvironmentCompat;
 
 import com.just.agentweb.AgentWeb;
 import com.just.agentweb.PermissionInterceptor;
 import com.just.agentweb.WebChromeClient;
 import com.just.agentweb.WebViewClient;
-import com.king.zxing.CameraScan;
+//import com.king.zxing.CameraScan;
+import com.qingdaofushan.homecaresas.activity.CamActivity;
 import com.qingdaofushan.homecaresas.event.EventData;
 import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.utils.Uri2PathUtil;
+import com.qingdaofushan.homecaresas.zxing.view.Constant;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
 
 /**
  * @Author pwxmax
@@ -36,6 +60,24 @@ public class WebViewActivity extends AppCompatActivity {
     protected AgentWeb mAgentWeb;
     private String url;
     private int REQUEST_CODE = 200;
+    // 拍照的requestCode
+    private static final int CAMERA_REQUEST_CODE = 0x00000010;
+    // 申请相机权限的requestCode
+    private static final int PERMISSION_CAMERA_REQUEST_CODE = 0x00000012;
+    /**
+     * 用于保存拍照图片的uri
+     */
+    private Uri mCameraUri;
+
+    /**
+     * 用于保存图片的文件路径,Android 10以下使用图片路径访问图片
+     */
+    private String mCameraImagePath;
+
+    /**
+     * 是否是Android 10以上手机
+     */
+    private boolean isAndroidQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -125,46 +167,23 @@ public class WebViewActivity extends AppCompatActivity {
 
     }
 
-    @Override
-    public void onRequestPermissionsResult(int requestCode,
-                                           String permissions[], int[] grantResults) {
-        switch (requestCode) {
-            case 1: {
-                // If request is cancelled, the result arrays are empty.
-                if (grantResults.length > 0
-                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-
-                    //ToastUtil.showToast("允许了");
-                    // permission was granted, yay! Do the
-                    // contacts-related task you need to do.
-//                    mAgentWeb.getJsEntraceAccess().quickCallJs("locationAllow", "1");
-                } else {
-                    //ToastUtil.showToast("不允许");
-                    // permission denied, boo! Disable the
-                    // functionality that depends on this permission.
-//                    mAgentWeb.getJsEntraceAccess().quickCallJs("locationAllow", "2");
-                }
-                return;
-            }
-
-            // other 'case' lines to check for other
-            // permissions this app might request
-        }
-
-    }
-
     @Override
     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         /**
          * 处理二维码扫描结果
          */
-
-        if(resultCode == RESULT_OK && data!=null){
-            switch (requestCode){
+        if (resultCode == RESULT_OK) {
+            switch (requestCode) {
                 case 200:
-                    String result = CameraScan.parseScanResult(data);
-                    mAgentWeb.getJsAccessEntrace().quickCallJs("QRCode", result);
+                    Bundle bundle = data.getExtras();
+                    String scanResult = bundle.getString(Constant.INTENT_EXTRA_KEY_QR_SCAN);
+//                    String result = CameraScan.parseScanResult(data);
+                    mAgentWeb.getJsAccessEntrace().quickCallJs("QRCode", scanResult);
+                    break;
+                case CAMERA_REQUEST_CODE:
+                    String uploadpath = Uri2PathUtil.getRealPathFromUri(this, mCameraUri);
+                    mAgentWeb.getJsAccessEntrace().quickCallJs("Camera", uploadpath);
                     break;
             }
         }
@@ -172,11 +191,148 @@ public class WebViewActivity extends AppCompatActivity {
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void stopServiceEvent(EventData eventData) {
-        if (eventData.getCode()==1) {
+        if (eventData.getCode() == 1) {
             mAgentWeb.getJsAccessEntrace().quickCallJs("StartService", "1");
-        }else if(eventData.getCode()==2){
+        } else if (eventData.getCode() == 2) {
             mAgentWeb.getJsAccessEntrace().quickCallJs("StartService", "2");
+        }else if(eventData.getCode() == 3){
+            checkPermissionAndCamera();
+        }
+    }
+
+    /**
+     * 检查权限并拍照。
+     * 调用相机前先检查权限。
+     */
+    private void checkPermissionAndCamera() {
+        int hasCameraPermission = ContextCompat.checkSelfPermission(getApplication(),
+                Manifest.permission.CAMERA);
+        if (hasCameraPermission == PackageManager.PERMISSION_GRANTED) {
+            //有权限,调起相机拍照。
+            openCamera();
+        } else {
+            //没有权限,申请权限。
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
+                    PERMISSION_CAMERA_REQUEST_CODE);
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                    PERMISSION_CAMERA_REQUEST_CODE);
+        }
+    }
+
+    /**
+     * 处理权限申请的回调。
+     *
+     * @param requestCode
+     * @param permissions
+     * @param grantResults
+     */
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        if (requestCode == PERMISSION_CAMERA_REQUEST_CODE) {
+            if (grantResults.length > 0
+                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                //允许权限,有调起相机拍照。
+                openCamera();
+            } else {
+                //拒绝权限,弹出提示框。
+                Toast.makeText(this, "拍照权限被拒绝", Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+
+    /**
+     * 调起相机拍照
+     */
+    private void openCamera() {
+        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        // 判断是否有相机
+        if (captureIntent.resolveActivity(getPackageManager()) != null) {
+            File photoFile = null;
+            Uri photoUri = null;
+
+            if (isAndroidQ) {
+                // 适配android 10
+                photoUri = createImageUri();
+            } else {
+                try {
+                    photoFile = createImageFile();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+                if (photoFile != null) {
+                    mCameraImagePath = photoFile.getAbsolutePath();
+                    System.out.println("path = " + mCameraImagePath);
+                    photoUri = FileProvider.getUriForFile(WebViewActivity.this,
+                            "com.qingdaofushan.homecaresas.fileprovider", photoFile);
+                }
+            }
+
+            System.out.println("photoUri = " + photoUri);
+            mCameraUri = photoUri;
+            if (photoUri != null) {
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                    List<ResolveInfo> resInfoList = getPackageManager()
+                            .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
+                    for (ResolveInfo resolveInfo : resInfoList) {
+                        String packageName = resolveInfo.activityInfo.packageName;
+                        grantUriPermission(packageName, photoUri,
+                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                    }
+                }
+                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
+                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);
+            }
+        }
+    }
+
+    /**
+     * 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
+     *
+     * @return 图片的uri
+     */
+    private Uri createImageUri() {
+        //设置保存参数到ContentValues中
+        ContentValues contentValues = new ContentValues();
+        //设置文件名
+        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() + "");
+        //兼容Android Q和以下版本
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            //android Q中不再使用DATA字段,而用RELATIVE_PATH代替
+            //TODO RELATIVE_PATH是相对路径不是绝对路径;照片存储的地方为:内部存储/Pictures/preventpro
+            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/preventpro");
+        }
+        //设置文件类型
+        contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG");
+        //执行insert操作,向系统文件夹中添加文件
+        //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
+        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
+        return uri;
+
+    }
+
+    /**
+     * 创建保存图片的文件
+     *
+     * @return
+     * @throws IOException
+     */
+    private File createImageFile() throws IOException {
+        String imageName = new SimpleDateFormat("yyyyMMdd_HHmmss",
+                Locale.getDefault()).format(new Date()) + ".jpg";
+//        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+//        File storageDir = Environment.getExternalStoragePublicDirectory(
+//                Environment.DIRECTORY_PICTURES);
+        File storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+                + File.separator + "Pictures" + File.separator + "abc");
+        if (!storageDir.exists()) storageDir.mkdirs();
+
+        File tempFile = new File(storageDir, imageName);
+        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
+            return null;
         }
+        return tempFile;
     }
 
     @Override

+ 387 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/activity/CaptureActivity.java

@@ -0,0 +1,387 @@
+package com.qingdaofushan.homecaresas.zxing.activity;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.text.TextUtils;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Result;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeReader;
+import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.zxing.camera.CameraManager;
+import com.qingdaofushan.homecaresas.zxing.decoding.CaptureActivityHandler;
+import com.qingdaofushan.homecaresas.zxing.decoding.InactivityTimer;
+import com.qingdaofushan.homecaresas.zxing.decoding.RGBLuminanceSource;
+import com.qingdaofushan.homecaresas.zxing.view.Constant;
+import com.qingdaofushan.homecaresas.zxing.view.UriUtil;
+import com.qingdaofushan.homecaresas.zxing.view.ViewfinderView;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+/**
+ * Initial the camera
+ *
+ * @author Ryan.Tang
+ */
+public class CaptureActivity extends AppCompatActivity implements Callback {
+
+    private static final int REQUEST_CODE_SCAN_GALLERY = 100;
+
+    private CaptureActivityHandler handler;
+    private ViewfinderView viewfinderView;
+    private ImageButton back;
+    private ImageButton btnFlash;
+    private Button btnAlbum; // 相册
+    private boolean isFlashOn = false;
+    private boolean hasSurface;
+    private Vector<BarcodeFormat> decodeFormats;
+    private String characterSet;
+    private InactivityTimer inactivityTimer;
+    private MediaPlayer mediaPlayer;
+    private boolean playBeep;
+    private static final float BEEP_VOLUME = 0.10f;
+    private boolean vibrate;
+    private ProgressDialog mProgress;
+    private String photo_path;
+    private Bitmap scanBitmap;
+    private RelativeLayout title_up;
+    //	private Button cancelScanButton;
+
+    /**
+     * Called when the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_scanner);
+        CameraManager.init(getApplication());
+        viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_content);
+        btnFlash = (ImageButton) findViewById(R.id.btn_flash);
+        btnFlash.setOnClickListener(flashListener);
+
+//        btnAlbum = (Button) findViewById(R.id.btn_album);
+//        btnAlbum.setOnClickListener(albumOnClick);
+
+//		cancelScanButton = (Button) this.findViewById(R.id.btn_cancel_scan);
+        hasSurface = false;
+        inactivityTimer = new InactivityTimer(this);
+
+    }
+
+    private View.OnClickListener albumOnClick = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            //打开手机中的相册
+            Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT); //"android.intent.action.GET_CONTENT"
+            innerIntent.setType("image/*");
+            startActivityForResult(innerIntent, REQUEST_CODE_SCAN_GALLERY);
+        }
+    };
+
+
+    @Override
+    protected void onActivityResult(final int requestCode, int resultCode, Intent data) {
+        if (resultCode == RESULT_OK) {
+            switch (requestCode) {
+                case REQUEST_CODE_SCAN_GALLERY:
+                    handleAlbumPic(data);
+                    break;
+            }
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    /**
+     * 处理选择的图片
+     *
+     * @param data
+     */
+    private void handleAlbumPic(Intent data) {
+        //获取选中图片的路径
+        photo_path = UriUtil.getRealPathFromUri(CaptureActivity.this, data.getData());
+
+        mProgress = new ProgressDialog(CaptureActivity.this);
+        mProgress.setMessage("正在扫描...");
+        mProgress.setCancelable(false);
+        mProgress.show();
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mProgress.dismiss();
+                Result result = scanningImage(photo_path);
+                if (result != null) {
+                    Intent resultIntent = new Intent();
+                    Bundle bundle = new Bundle();
+                    bundle.putString(Constant.INTENT_EXTRA_KEY_QR_SCAN, result.getText());
+
+                    resultIntent.putExtras(bundle);
+                    CaptureActivity.this.setResult(RESULT_OK, resultIntent);
+                    finish();
+                } else {
+                    Toast.makeText(CaptureActivity.this, "识别失败", Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+    }
+
+    /**
+     * 扫描二维码图片的方法
+     *
+     * @param path
+     * @return
+     */
+    public Result scanningImage(String path) {
+        if (TextUtils.isEmpty(path)) {
+            return null;
+        }
+        Hashtable<DecodeHintType, String> hints = new Hashtable<>();
+        hints.put(DecodeHintType.CHARACTER_SET, "UTF8"); //设置二维码内容的编码
+
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true; // 先获取原大小
+        scanBitmap = BitmapFactory.decodeFile(path, options);
+        options.inJustDecodeBounds = false; // 获取新的大小
+        int sampleSize = (int) (options.outHeight / (float) 200);
+        if (sampleSize <= 0)
+            sampleSize = 1;
+        options.inSampleSize = sampleSize;
+        scanBitmap = BitmapFactory.decodeFile(path, options);
+        RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);
+        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
+        QRCodeReader reader = new QRCodeReader();
+        try {
+            return reader.decode(bitmap1, hints);
+        } catch (NotFoundException e) {
+            e.printStackTrace();
+        } catch (ChecksumException e) {
+            e.printStackTrace();
+        } catch (FormatException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.scanner_view);
+        SurfaceHolder surfaceHolder = surfaceView.getHolder();
+        if (hasSurface) {
+            initCamera(surfaceHolder);
+        } else {
+            surfaceHolder.addCallback(this);
+            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+        }
+        decodeFormats = null;
+        characterSet = null;
+
+        playBeep = true;
+        AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
+        if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
+            playBeep = false;
+        }
+        initBeepSound();
+        vibrate = true;
+
+        //quit the scan view
+//		cancelScanButton.setOnClickListener(new OnClickListener() {
+//
+//			@Override
+//			public void onClick(View v) {
+//				CaptureActivity.this.finish();
+//			}
+//		});
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (handler != null) {
+            handler.quitSynchronously();
+            handler = null;
+        }
+        CameraManager.get().closeDriver();
+    }
+
+    @Override
+    protected void onDestroy() {
+        inactivityTimer.shutdown();
+        super.onDestroy();
+    }
+
+    /**
+     * Handler scan result
+     *
+     * @param result
+     * @param barcode
+     */
+    public void handleDecode(Result result, Bitmap barcode) {
+        inactivityTimer.onActivity();
+        playBeepSoundAndVibrate();
+        String resultString = result.getText();
+        //FIXME
+        if (TextUtils.isEmpty(resultString)) {
+            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
+        } else {
+            Intent resultIntent = new Intent();
+            Bundle bundle = new Bundle();
+            bundle.putString(Constant.INTENT_EXTRA_KEY_QR_SCAN, resultString);
+            System.out.println("sssssssssssssssss scan 0 = " + resultString);
+            // 不能使用Intent传递大于40kb的bitmap,可以使用一个单例对象存储这个bitmap
+//            bundle.putParcelable("bitmap", barcode);
+//            Logger.d("saomiao",resultString);
+            resultIntent.putExtras(bundle);
+            this.setResult(RESULT_OK, resultIntent);
+        }
+        CaptureActivity.this.finish();
+    }
+
+    private void initCamera(SurfaceHolder surfaceHolder) {
+        try {
+            CameraManager.get().openDriver(surfaceHolder);
+        } catch (IOException ioe) {
+            return;
+        } catch (RuntimeException e) {
+            return;
+        }
+        if (handler == null) {
+            handler = new CaptureActivityHandler(this, decodeFormats,
+                    characterSet);
+        }
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width,
+                               int height) {
+
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        if (!hasSurface) {
+            hasSurface = true;
+            initCamera(holder);
+        }
+
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        hasSurface = false;
+
+    }
+
+    public ViewfinderView getViewfinderView() {
+        return viewfinderView;
+    }
+
+    public Handler getHandler() {
+        return handler;
+    }
+
+    public void drawViewfinder() {
+        viewfinderView.drawViewfinder();
+
+    }
+
+    private void initBeepSound() {
+        if (playBeep && mediaPlayer == null) {
+            // The volume on STREAM_SYSTEM is not adjustable, and users found it
+            // too loud,
+            // so we now play on the music stream.
+            setVolumeControlStream(AudioManager.STREAM_MUSIC);
+            mediaPlayer = new MediaPlayer();
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mediaPlayer.setOnCompletionListener(beepListener);
+
+            AssetFileDescriptor file = getResources().openRawResourceFd(
+                    R.raw.beep);
+            try {
+                mediaPlayer.setDataSource(file.getFileDescriptor(),
+                        file.getStartOffset(), file.getLength());
+                file.close();
+                mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
+                mediaPlayer.prepare();
+            } catch (IOException e) {
+                mediaPlayer = null;
+            }
+        }
+    }
+
+    private static final long VIBRATE_DURATION = 200L;
+
+    private void playBeepSoundAndVibrate() {
+        if (playBeep && mediaPlayer != null) {
+            mediaPlayer.start();
+        }
+        if (vibrate) {
+            Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+            vibrator.vibrate(VIBRATE_DURATION);
+        }
+    }
+
+    /**
+     * When the beep has finished playing, rewind to queue up another one.
+     */
+    private final OnCompletionListener beepListener = new OnCompletionListener() {
+        public void onCompletion(MediaPlayer mediaPlayer) {
+            mediaPlayer.seekTo(0);
+        }
+    };
+
+    /**
+     * 闪光灯开关按钮
+     */
+    private View.OnClickListener flashListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            try {
+                boolean isSuccess = CameraManager.get().setFlashLight(!isFlashOn);
+                if (!isSuccess) {
+                    Toast.makeText(CaptureActivity.this, "暂时无法开启闪光灯", Toast.LENGTH_SHORT).show();
+                    return;
+                }
+                if (isFlashOn) {
+                    // 关闭闪光灯
+                    btnFlash.setImageResource(R.drawable.flash_off);
+                    isFlashOn = false;
+                } else {
+                    // 开启闪光灯
+                    btnFlash.setImageResource(R.drawable.flash_on);
+                    isFlashOn = true;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    };
+}

+ 48 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/AutoFocusCallback.java

@@ -0,0 +1,48 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+final class AutoFocusCallback implements Camera.AutoFocusCallback {
+
+  private static final String TAG = AutoFocusCallback.class.getSimpleName();
+
+  private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
+
+  private Handler autoFocusHandler;
+  private int autoFocusMessage;
+
+  void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
+    this.autoFocusHandler = autoFocusHandler;
+    this.autoFocusMessage = autoFocusMessage;
+  }
+
+  public void onAutoFocus(boolean success, Camera camera) {
+    if (autoFocusHandler != null) {
+      Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
+      autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
+      autoFocusHandler = null;
+    } else {
+      Log.d(TAG, "Got auto-focus callback, but no handler for it");
+    }
+  }
+
+}

+ 278 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/CameraConfigurationManager.java

@@ -0,0 +1,278 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import java.util.regex.Pattern;
+
+final class CameraConfigurationManager {
+
+  private static final String TAG = CameraConfigurationManager.class.getSimpleName();
+
+  private static final int TEN_DESIRED_ZOOM = 27;
+  private static final int DESIRED_SHARPNESS = 30;
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  private final Context context;
+  private Point screenResolution;
+  private Point cameraResolution;
+  private int previewFormat;
+  private String previewFormatString;
+
+  CameraConfigurationManager(Context context) {
+    this.context = context;
+  }
+
+  /**
+   * Reads, one time, values from the camera that are needed by the app.
+   */
+  void initFromCameraParameters(Camera camera) {
+    Camera.Parameters parameters = camera.getParameters();
+    previewFormat = parameters.getPreviewFormat();
+    previewFormatString = parameters.get("preview-format");
+    Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
+    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+    Display display = manager.getDefaultDisplay();
+    screenResolution = new Point(display.getWidth(), display.getHeight());
+    Log.d(TAG, "Screen resolution: " + screenResolution);
+
+    //图片拉伸
+    Point screenResolutionForCamera = new Point();
+    screenResolutionForCamera.x = screenResolution.x;
+    screenResolutionForCamera.y = screenResolution.y;
+    // preview size is always something like 480*320, other 320*480
+    if (screenResolution.x < screenResolution.y) {
+      screenResolutionForCamera.x = screenResolution.y;
+      screenResolutionForCamera.y = screenResolution.x;
+    }
+
+    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
+    Log.d(TAG, "Camera resolution: " + screenResolution);
+
+  }
+
+  /**
+   * Sets the camera up to take preview images which are used for both preview and decoding.
+   * We detect the preview format here so that buildLuminanceSource() can build an appropriate
+   * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
+   * and the planar Y can be used for barcode scanning without a copy in some cases.
+   */
+  void setDesiredCameraParameters(Camera camera) {
+    Camera.Parameters parameters = camera.getParameters();
+    Log.d(TAG, "Setting preview size: " + cameraResolution);
+    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+    setFlash(parameters);
+    setZoom(parameters);
+    //setSharpness(parameters);
+    //modify here
+    camera.setDisplayOrientation(90);
+    camera.setParameters(parameters);
+  }
+
+  Point getCameraResolution() {
+    return cameraResolution;
+  }
+
+  Point getScreenResolution() {
+    return screenResolution;
+  }
+
+  int getPreviewFormat() {
+    return previewFormat;
+  }
+
+  String getPreviewFormatString() {
+    return previewFormatString;
+  }
+
+  private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
+
+    String previewSizeValueString = parameters.get("preview-size-values");
+    // saw this on Xperia
+    if (previewSizeValueString == null) {
+      previewSizeValueString = parameters.get("preview-size-value");
+    }
+
+    Point cameraResolution = null;
+
+    if (previewSizeValueString != null) {
+      Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
+      cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
+    }
+
+    if (cameraResolution == null) {
+      // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
+      cameraResolution = new Point(
+          (screenResolution.x >> 3) << 3,
+          (screenResolution.y >> 3) << 3);
+    }
+
+    return cameraResolution;
+  }
+
+  private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
+    int bestX = 0;
+    int bestY = 0;
+    int diff = Integer.MAX_VALUE;
+    for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
+
+      previewSize = previewSize.trim();
+      int dimPosition = previewSize.indexOf('x');
+      if (dimPosition < 0) {
+        Log.w(TAG, "Bad preview-size: " + previewSize);
+        continue;
+      }
+
+      int newX;
+      int newY;
+      try {
+        newX = Integer.parseInt(previewSize.substring(0, dimPosition));
+        newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad preview-size: " + previewSize);
+        continue;
+      }
+
+      int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
+      if (newDiff == 0) {
+        bestX = newX;
+        bestY = newY;
+        break;
+      } else if (newDiff < diff) {
+        bestX = newX;
+        bestY = newY;
+        diff = newDiff;
+      }
+
+    }
+
+    if (bestX > 0 && bestY > 0) {
+      return new Point(bestX, bestY);
+    }
+    return null;
+  }
+
+  private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
+    int tenBestValue = 0;
+    for (String stringValue : COMMA_PATTERN.split(stringValues)) {
+      stringValue = stringValue.trim();
+      double value;
+      try {
+        value = Double.parseDouble(stringValue);
+      } catch (NumberFormatException nfe) {
+        return tenDesiredZoom;
+      }
+      int tenValue = (int) (10.0 * value);
+      if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
+        tenBestValue = tenValue;
+      }
+    }
+    return tenBestValue;
+  }
+
+  private void setFlash(Camera.Parameters parameters) {
+    // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
+    // And this is a hack-hack to work around a different value on the Behold II
+    // Restrict Behold II check to Cupcake, per Samsung's advice
+    //if (Build.MODEL.contains("Behold II") &&
+    //    CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
+    if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
+      parameters.set("flash-value", 1);
+    } else {
+      parameters.set("flash-value", 2);
+    }
+    // This is the standard setting to turn the flash off that all devices should honor.
+    parameters.set("flash-mode", "off");
+  }
+
+  private void setZoom(Camera.Parameters parameters) {
+
+    String zoomSupportedString = parameters.get("zoom-supported");
+    if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
+      return;
+    }
+
+    int tenDesiredZoom = TEN_DESIRED_ZOOM;
+
+    String maxZoomString = parameters.get("max-zoom");
+    if (maxZoomString != null) {
+      try {
+        int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
+        if (tenDesiredZoom > tenMaxZoom) {
+          tenDesiredZoom = tenMaxZoom;
+        }
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad max-zoom: " + maxZoomString);
+      }
+    }
+
+    String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
+    if (takingPictureZoomMaxString != null) {
+      try {
+        int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
+        if (tenDesiredZoom > tenMaxZoom) {
+          tenDesiredZoom = tenMaxZoom;
+        }
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
+      }
+    }
+
+    String motZoomValuesString = parameters.get("mot-zoom-values");
+    if (motZoomValuesString != null) {
+      tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
+    }
+
+    String motZoomStepString = parameters.get("mot-zoom-step");
+    if (motZoomStepString != null) {
+      try {
+        double motZoomStep = Double.parseDouble(motZoomStepString.trim());
+        int tenZoomStep = (int) (10.0 * motZoomStep);
+        if (tenZoomStep > 1) {
+          tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
+        }
+      } catch (NumberFormatException nfe) {
+        // continue
+      }
+    }
+
+    // Set zoom. This helps encourage the user to pull back.
+    // Some devices like the Behold have a zoom parameter
+    if (maxZoomString != null || motZoomValuesString != null) {
+      parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
+    }
+
+    // Most devices, like the Hero, appear to expose this zoom parameter.
+    // It takes on values like "27" which appears to mean 2.7x zoom
+    if (takingPictureZoomMaxString != null) {
+      parameters.set("taking-picture-zoom", tenDesiredZoom);
+    }
+  }
+
+	public static int getDesiredSharpness() {
+		return DESIRED_SHARPNESS;
+	}
+
+}

+ 398 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/CameraManager.java

@@ -0,0 +1,398 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Handler;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * This object wraps the Camera service object and expects to be the only one talking to it. The
+ * implementation encapsulates the steps needed to take preview-sized images, which are used for
+ * both preview and decoding.
+ */
+public final class CameraManager {
+
+    private static final String TAG = CameraManager.class.getSimpleName();
+
+    private static final int MIN_FRAME_WIDTH = 240;
+    private static final int MIN_FRAME_HEIGHT = 240;
+    private static final int MAX_FRAME_WIDTH = 480;
+    private static final int MAX_FRAME_HEIGHT = 360;
+
+    private static CameraManager cameraManager;
+
+    static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
+
+    static {
+        int sdkInt;
+        try {
+            sdkInt = Integer.parseInt(Build.VERSION.SDK);
+        } catch (NumberFormatException nfe) {
+            // Just to be safe
+            sdkInt = 10000;
+        }
+        SDK_INT = sdkInt;
+    }
+
+    private final Context context;
+    private final CameraConfigurationManager configManager;
+    private Camera camera;
+    private Rect framingRect;
+    private Rect framingRectInPreview;
+    private boolean initialized;
+    private boolean previewing;
+    private final boolean useOneShotPreviewCallback;
+    /**
+     * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
+     * clear the handler so it will only receive one message.
+     */
+    private final PreviewCallback previewCallback;
+    /**
+     * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
+     */
+    private final AutoFocusCallback autoFocusCallback;
+
+    /**
+     * Initializes this static object with the Context of the calling Activity.
+     *
+     * @param context The Activity which wants to use the camera.
+     */
+    public static void init(Context context) {
+        if (cameraManager == null) {
+            cameraManager = new CameraManager(context);
+        }
+    }
+
+    /**
+     * Gets the CameraManager singleton instance.
+     *
+     * @return A reference to the CameraManager singleton.
+     */
+    public static CameraManager get() {
+        return cameraManager;
+    }
+
+    private CameraManager(Context context) {
+
+        this.context = context;
+        this.configManager = new CameraConfigurationManager(context);
+
+        // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
+        // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
+        // the more efficient one shot callback, as the older one can swamp the system and cause it
+        // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
+        //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
+        useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake
+
+        previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
+        autoFocusCallback = new AutoFocusCallback();
+    }
+
+    /**
+     * Opens the camera driver and initializes the hardware parameters.
+     *
+     * @param holder The surface object which the camera will draw preview frames into.
+     * @throws IOException Indicates the camera driver failed to open.
+     */
+    public void openDriver(SurfaceHolder holder) throws IOException {
+        if (camera == null) {
+            camera = Camera.open();
+            if (camera == null) {
+                throw new IOException();
+            }
+            camera.setPreviewDisplay(holder);
+
+            if (!initialized) {
+                initialized = true;
+                configManager.initFromCameraParameters(camera);
+            }
+            configManager.setDesiredCameraParameters(camera);
+
+            //FIXME
+            //     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+            //�Ƿ�ʹ��ǰ��
+//      if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
+//        FlashlightManager.enableFlashlight();
+//      }
+            FlashlightManager.enableFlashlight();
+        }
+    }
+
+    /**
+     * Closes the camera driver if still in use.
+     */
+    public void closeDriver() {
+        if (camera != null) {
+            FlashlightManager.disableFlashlight();
+            camera.release();
+            camera = null;
+        }
+    }
+
+    /**
+     * Asks the camera hardware to begin drawing preview frames to the screen.
+     */
+    public void startPreview() {
+        if (camera != null && !previewing) {
+            camera.startPreview();
+            previewing = true;
+        }
+    }
+
+    /**
+     * Tells the camera to stop drawing preview frames.
+     */
+    public void stopPreview() {
+        if (camera != null && previewing) {
+            if (!useOneShotPreviewCallback) {
+                camera.setPreviewCallback(null);
+            }
+            camera.stopPreview();
+            previewCallback.setHandler(null, 0);
+            autoFocusCallback.setHandler(null, 0);
+            previewing = false;
+        }
+    }
+
+    /**
+     * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
+     * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
+     * respectively.
+     *
+     * @param handler The handler to send the message to.
+     * @param message The what field of the message to be sent.
+     */
+    public void requestPreviewFrame(Handler handler, int message) {
+        if (camera != null && previewing) {
+            previewCallback.setHandler(handler, message);
+            if (useOneShotPreviewCallback) {
+                camera.setOneShotPreviewCallback(previewCallback);
+            } else {
+                camera.setPreviewCallback(previewCallback);
+            }
+        }
+    }
+
+    /**
+     * Asks the camera hardware to perform an autofocus.
+     *
+     * @param handler The Handler to notify when the autofocus completes.
+     * @param message The message to deliver.
+     */
+    public void requestAutoFocus(Handler handler, int message) {
+        if (camera != null && previewing) {
+            autoFocusCallback.setHandler(handler, message);
+            //Log.d(TAG, "Requesting auto-focus callback");
+            camera.autoFocus(autoFocusCallback);
+        }
+    }
+
+    /**
+     * Calculates the framing rect which the UI should draw to show the user where to place the
+     * barcode. This target helps with alignment as well as forces the user to hold the device
+     * far enough away to ensure the image will be in focus.
+     *
+     * @return The rectangle to draw on screen in window coordinates.
+     */
+    public Rect getFramingRect() {
+        Point screenResolution = configManager.getScreenResolution();
+        if (screenResolution == null)
+            return null;
+        if (framingRect == null) {
+            if (camera == null) {
+                return null;
+            }
+
+            //修改之后
+            int width = screenResolution.x * 7 / 10;
+            int height = screenResolution.y * 7 / 10;
+
+            if (height >= width) { //竖屏
+                height = width;
+            } else { //黑屏
+                width = height;
+            }
+
+            int leftOffset = (screenResolution.x - width) / 2;
+            int topOffset = (screenResolution.y - height) / 3;
+            framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+
+        }
+        return framingRect;
+    }
+//  public Rect getFramingRect() {
+//    Point screenResolution = configManager.getScreenResolution();
+//    if (framingRect == null) {
+//      if (camera == null) {
+//        return null;
+//      }
+//      int width = screenResolution.x * 3 / 4;
+//      if (width < MIN_FRAME_WIDTH) {
+//        width = MIN_FRAME_WIDTH;
+//      } else if (width > MAX_FRAME_WIDTH) {
+//        width = MAX_FRAME_WIDTH;
+//      }
+//      int height = screenResolution.y * 3 / 4;
+//      if (height < MIN_FRAME_HEIGHT) {
+//        height = MIN_FRAME_HEIGHT;
+//      } else if (height > MAX_FRAME_HEIGHT) {
+//        height = MAX_FRAME_HEIGHT;
+//      }
+//      int leftOffset = (screenResolution.x - width) / 2;
+//      int topOffset = (screenResolution.y - height) / 2;
+//      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+//      Log.d(TAG, "Calculated framing rect: " + framingRect);
+//    }
+//    return framingRect;
+//  }
+
+    /**
+     * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
+     * not UI / screen.
+     */
+    public Rect getFramingRectInPreview() {
+        if (framingRectInPreview == null) {
+            Rect rect = new Rect(getFramingRect());
+            Point cameraResolution = configManager.getCameraResolution();
+            Point screenResolution = configManager.getScreenResolution();
+            //modify here
+//      rect.left = rect.left * cameraResolution.x / screenResolution.x;
+//      rect.right = rect.right * cameraResolution.x / screenResolution.x;
+//      rect.top = rect.top * cameraResolution.y / screenResolution.y;
+//      rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
+            rect.left = rect.left * cameraResolution.y / screenResolution.x;
+            rect.right = rect.right * cameraResolution.y / screenResolution.x;
+            rect.top = rect.top * cameraResolution.x / screenResolution.y;
+            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
+            framingRectInPreview = rect;
+        }
+        return framingRectInPreview;
+    }
+
+    /**
+     * Converts the result points from still resolution coordinates to screen coordinates.
+     *
+     * @param points The points returned by the Reader subclass through Result.getResultPoints().
+     * @return An array of Points scaled to the size of the framing rect and offset appropriately
+     *         so they can be drawn in screen coordinates.
+     */
+  /*
+  public Point[] convertResultPoints(ResultPoint[] points) {
+    Rect frame = getFramingRectInPreview();
+    int count = points.length;
+    Point[] output = new Point[count];
+    for (int x = 0; x < count; x++) {
+      output[x] = new Point();
+      output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
+      output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
+    }
+    return output;
+  }
+   */
+
+    /**
+     * A factory method to build the appropriate LuminanceSource object based on the format
+     * of the preview buffers, as described by Camera.Parameters.
+     *
+     * @param data   A preview frame.
+     * @param width  The width of the image.
+     * @param height The height of the image.
+     * @return A PlanarYUVLuminanceSource instance.
+     */
+    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
+        Rect rect = getFramingRectInPreview();
+        int previewFormat = configManager.getPreviewFormat();
+        String previewFormatString = configManager.getPreviewFormatString();
+        switch (previewFormat) {
+            // This is the standard Android format which all devices are REQUIRED to support.
+            // In theory, it's the only one we should ever care about.
+            case PixelFormat.YCbCr_420_SP:
+                // This format has never been seen in the wild, but is compatible as we only care
+                // about the Y channel, so allow it.
+            case PixelFormat.YCbCr_422_SP:
+                return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height);
+            default:
+                // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
+                // Fortunately, it too has all the Y data up front, so we can read it.
+                if ("yuv420p".equals(previewFormatString)) {
+                    return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height);
+                }
+        }
+        throw new IllegalArgumentException("Unsupported picture format: " +
+                previewFormat + '/' + previewFormatString);
+    }
+
+    public Context getContext() {
+        return context;
+    }
+
+    /**
+     * 打开或关闭闪光灯
+     * @param isOpen 是否开启闪光灯
+     * @return boolean 操作成功/失败。
+     */
+    public boolean setFlashLight(boolean isOpen) {
+        if (camera == null || !previewing) {
+            return false;
+        }
+        Camera.Parameters parameters = camera.getParameters();
+        if (parameters == null) {
+            return false;
+        }
+        List<String> flashModes = parameters.getSupportedFlashModes();
+        // 检查手机是否有闪光灯
+        if (null == flashModes || 0 == flashModes.size()) {
+            // 没有闪光灯则返回
+            return false;
+        }
+        String flashMode = parameters.getFlashMode();
+        if (isOpen) {
+            if (Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
+                return true;
+            }
+            // 开启
+            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+                camera.setParameters(parameters);
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
+                return true;
+            }
+            // 关闭
+            if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+                camera.setParameters(parameters);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+}

+ 150 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/FlashlightManager.java

@@ -0,0 +1,150 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.os.IBinder;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class is used to activate the weak light on some camera phones (not flash)
+ * in order to illuminate surfaces for scanning. There is no official way to do this,
+ * but, classes which allow access to this function still exist on some devices.
+ * This therefore proceeds through a great deal of reflection.
+ *
+ * See <a href="http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/">
+ * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/</a> and
+ * <a href="http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java">
+ * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java</a>.
+ * Thanks to Ryan Alford for pointing out the availability of this class.
+ */
+final class FlashlightManager {
+
+  private static final String TAG = FlashlightManager.class.getSimpleName();
+
+  private static final Object iHardwareService;
+  private static final Method setFlashEnabledMethod;
+  static {
+    iHardwareService = getHardwareService();
+    setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);
+    if (iHardwareService == null) {
+      Log.v(TAG, "This device does supports control of a flashlight");
+    } else {
+      Log.v(TAG, "This device does not support control of a flashlight");
+    }
+  }
+
+  private FlashlightManager() {
+  }
+
+  /**
+   * �����������ƿ���
+   */
+  //FIXME
+  static void enableFlashlight() {
+    setFlashlight(false);
+  }
+
+  static void disableFlashlight() {
+    setFlashlight(false);
+  }
+
+  private static Object getHardwareService() {
+    Class<?> serviceManagerClass = maybeForName("android.os.ServiceManager");
+    if (serviceManagerClass == null) {
+      return null;
+    }
+
+    Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class);
+    if (getServiceMethod == null) {
+      return null;
+    }
+
+    Object hardwareService = invoke(getServiceMethod, null, "hardware");
+    if (hardwareService == null) {
+      return null;
+    }
+
+    Class<?> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");
+    if (iHardwareServiceStubClass == null) {
+      return null;
+    }
+
+    Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class);
+    if (asInterfaceMethod == null) {
+      return null;
+    }
+
+    return invoke(asInterfaceMethod, null, hardwareService);
+  }
+
+  private static Method getSetFlashEnabledMethod(Object iHardwareService) {
+    if (iHardwareService == null) {
+      return null;
+    }
+    Class<?> proxyClass = iHardwareService.getClass();
+    return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class);
+  }
+
+  private static Class<?> maybeForName(String name) {
+    try {
+      return Class.forName(name);
+    } catch (ClassNotFoundException cnfe) {
+      // OK
+      return null;
+    } catch (RuntimeException re) {
+      Log.w(TAG, "Unexpected error while finding class " + name, re);
+      return null;
+    }
+  }
+
+  private static Method maybeGetMethod(Class<?> clazz, String name, Class<?>... argClasses) {
+    try {
+      return clazz.getMethod(name, argClasses);
+    } catch (NoSuchMethodException nsme) {
+      // OK
+      return null;
+    } catch (RuntimeException re) {
+      Log.w(TAG, "Unexpected error while finding method " + name, re);
+      return null;
+    }
+  }
+
+  private static Object invoke(Method method, Object instance, Object... args) {
+    try {
+      return method.invoke(instance, args);
+    } catch (IllegalAccessException e) {
+      Log.w(TAG, "Unexpected error while invoking " + method, e);
+      return null;
+    } catch (InvocationTargetException e) {
+      Log.w(TAG, "Unexpected error while invoking " + method, e.getCause());
+      return null;
+    } catch (RuntimeException re) {
+      Log.w(TAG, "Unexpected error while invoking " + method, re);
+      return null;
+    }
+  }
+
+  private static void setFlashlight(boolean active) {
+    if (iHardwareService != null) {
+      invoke(setFlashEnabledMethod, iHardwareService, active);
+    }
+  }
+
+}

+ 133 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/PlanarYUVLuminanceSource.java

@@ -0,0 +1,133 @@
+/*
+ * Copyright 2009 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.graphics.Bitmap;
+
+import com.google.zxing.LuminanceSource;
+
+/**
+ * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
+ * with the option to crop to a rectangle within the full data. This can be used to exclude
+ * superfluous pixels around the perimeter and speed up decoding.
+ *
+ * It works for any pixel format where the Y channel is planar and appears first, including
+ * YCbCr_420_SP and YCbCr_422_SP.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class PlanarYUVLuminanceSource extends LuminanceSource {
+  private final byte[] yuvData;
+  private final int dataWidth;
+  private final int dataHeight;
+  private final int left;
+  private final int top;
+
+  public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
+      int width, int height) {
+    super(width, height);
+
+    if (left + width > dataWidth || top + height > dataHeight) {
+      throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
+    }
+
+    this.yuvData = yuvData;
+    this.dataWidth = dataWidth;
+    this.dataHeight = dataHeight;
+    this.left = left;
+    this.top = top;
+  }
+
+  @Override
+  public byte[] getRow(int y, byte[] row) {
+    if (y < 0 || y >= getHeight()) {
+      throw new IllegalArgumentException("Requested row is outside the image: " + y);
+    }
+    int width = getWidth();
+    if (row == null || row.length < width) {
+      row = new byte[width];
+    }
+    int offset = (y + top) * dataWidth + left;
+    System.arraycopy(yuvData, offset, row, 0, width);
+    return row;
+  }
+
+  @Override
+  public byte[] getMatrix() {
+    int width = getWidth();
+    int height = getHeight();
+
+    // If the caller asks for the entire underlying image, save the copy and give them the
+    // original data. The docs specifically warn that result.length must be ignored.
+    if (width == dataWidth && height == dataHeight) {
+      return yuvData;
+    }
+
+    int area = width * height;
+    byte[] matrix = new byte[area];
+    int inputOffset = top * dataWidth + left;
+
+    // If the width matches the full width of the underlying data, perform a single copy.
+    if (width == dataWidth) {
+      System.arraycopy(yuvData, inputOffset, matrix, 0, area);
+      return matrix;
+    }
+
+    // Otherwise copy one cropped row at a time.
+    byte[] yuv = yuvData;
+    for (int y = 0; y < height; y++) {
+      int outputOffset = y * width;
+      System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
+      inputOffset += dataWidth;
+    }
+    return matrix;
+  }
+
+  @Override
+  public boolean isCropSupported() {
+    return true;
+  }
+
+  public int getDataWidth() {
+    return dataWidth;
+  }
+
+  public int getDataHeight() {
+    return dataHeight;
+  }
+
+  public Bitmap renderCroppedGreyscaleBitmap() {
+    int width = getWidth();
+    int height = getHeight();
+    int[] pixels = new int[width * height];
+    byte[] yuv = yuvData;
+    int inputOffset = top * dataWidth + left;
+
+    for (int y = 0; y < height; y++) {
+      int outputOffset = y * width;
+      for (int x = 0; x < width; x++) {
+        int grey = yuv[inputOffset + x] & 0xff;
+        pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
+      }
+      inputOffset += dataWidth;
+    }
+
+    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+    return bitmap;
+  }
+}

+ 59 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/camera/PreviewCallback.java

@@ -0,0 +1,59 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.camera;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+final class PreviewCallback implements Camera.PreviewCallback {
+
+  private static final String TAG = PreviewCallback.class.getSimpleName();
+
+  private final CameraConfigurationManager configManager;
+  private final boolean useOneShotPreviewCallback;
+  private Handler previewHandler;
+  private int previewMessage;
+
+  PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
+    this.configManager = configManager;
+    this.useOneShotPreviewCallback = useOneShotPreviewCallback;
+  }
+
+  void setHandler(Handler previewHandler, int previewMessage) {
+    this.previewHandler = previewHandler;
+    this.previewMessage = previewMessage;
+  }
+
+  public void onPreviewFrame(byte[] data, Camera camera) {
+    Point cameraResolution = configManager.getCameraResolution();
+    if (!useOneShotPreviewCallback) {
+      camera.setPreviewCallback(null);
+    }
+    if (previewHandler != null) {
+      Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
+          cameraResolution.y, data);
+      message.sendToTarget();
+      previewHandler = null;
+    } else {
+      Log.d(TAG, "Got preview callback, but no handler for it");
+    }
+  }
+
+}

+ 139 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/CaptureActivityHandler.java

@@ -0,0 +1,139 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.zxing.activity.CaptureActivity;
+import com.qingdaofushan.homecaresas.zxing.camera.CameraManager;
+import com.qingdaofushan.homecaresas.zxing.view.ViewfinderResultPointCallback;
+
+import java.util.Vector;
+
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ */
+public final class CaptureActivityHandler extends Handler {
+
+    private static final String TAG = CaptureActivityHandler.class.getSimpleName();
+
+    private final CaptureActivity activity;
+    private final DecodeThread decodeThread;
+    private State state;
+
+    private enum State {
+        PREVIEW,
+        SUCCESS,
+        DONE
+    }
+
+    public CaptureActivityHandler(CaptureActivity activity, Vector<BarcodeFormat> decodeFormats,
+                                  String characterSet) {
+        this.activity = activity;
+        decodeThread = new DecodeThread(activity, decodeFormats, characterSet,
+                new ViewfinderResultPointCallback(activity.getViewfinderView()));
+        decodeThread.start();
+        state = State.SUCCESS;
+        // Start ourselves capturing previews and decoding.
+        CameraManager.get().startPreview();
+        restartPreviewAndDecode();
+    }
+
+    @Override
+    public void handleMessage(Message message) {
+        switch (message.what) {
+            case R.id.auto_focus:
+                //Log.d(TAG, "Got auto-focus message");
+                // When one auto focus pass finishes, start another. This is the closest thing to
+                // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
+                if (state == State.PREVIEW) {
+                    CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+                }
+                break;
+            case R.id.restart_preview:
+                Log.d(TAG, "Got restart preview message");
+                restartPreviewAndDecode();
+                break;
+            case R.id.decode_succeeded:
+                Log.d(TAG, "Got decode succeeded message");
+                state = State.SUCCESS;
+                Bundle bundle = message.getData();
+
+                /***********************************************************************/
+                Bitmap barcode = bundle == null ? null :
+                        (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);//���ñ����߳�
+
+                activity.handleDecode((Result) message.obj, barcode);//���ؽ��
+                /***********************************************************************/
+                break;
+            case R.id.decode_failed:
+                // We're decoding as fast as possible, so when one decode fails, start another.
+                state = State.PREVIEW;
+                CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+                break;
+            case R.id.return_scan_result:
+                Log.d(TAG, "Got return scan result message");
+                activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
+                activity.finish();
+                break;
+            case R.id.launch_product_query:
+                Log.d(TAG, "Got product query message");
+                String url = (String) message.obj;
+                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                activity.startActivity(intent);
+                break;
+        }
+    }
+
+    public void quitSynchronously() {
+        state = State.DONE;
+        CameraManager.get().stopPreview();
+        Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+        quit.sendToTarget();
+        try {
+            decodeThread.join();
+        } 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);
+    }
+
+    private void restartPreviewAndDecode() {
+        if (state == State.SUCCESS) {
+            state = State.PREVIEW;
+            CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+            CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+            activity.drawViewfinder();
+        }
+    }
+
+}

+ 104 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeFormatManager.java

@@ -0,0 +1,104 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import com.google.zxing.BarcodeFormat;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+final class DecodeFormatManager {
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  static final Vector<BarcodeFormat> PRODUCT_FORMATS;
+  static final Vector<BarcodeFormat> ONE_D_FORMATS;
+  static final Vector<BarcodeFormat> QR_CODE_FORMATS;
+  static final Vector<BarcodeFormat> DATA_MATRIX_FORMATS;
+  static {
+    PRODUCT_FORMATS = new Vector<BarcodeFormat>(5);
+    PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
+    PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
+    PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
+    PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
+    ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 4);
+    ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
+    ONE_D_FORMATS.add(BarcodeFormat.ITF);
+    QR_CODE_FORMATS = new Vector<BarcodeFormat>(1);
+    QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
+    DATA_MATRIX_FORMATS = new Vector<BarcodeFormat>(1);
+    DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);
+  }
+
+  private DecodeFormatManager() {}
+
+  static Vector<BarcodeFormat> parseDecodeFormats(Intent intent) {
+    List<String> scanFormats = null;
+    String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
+    if (scanFormatsString != null) {
+      scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+    }
+    return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+  }
+
+  static Vector<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
+    List<String> formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS);
+    if (formats != null && formats.size() == 1 && formats.get(0) != null){
+      formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
+    }
+    return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
+  }
+
+  private static Vector<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats,
+                                                          String decodeMode) {
+    if (scanFormats != null) {
+      Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
+      try {
+        for (String format : scanFormats) {
+          formats.add(BarcodeFormat.valueOf(format));
+        }
+        return formats;
+      } catch (IllegalArgumentException iae) {
+        // ignore it then
+      }
+    }
+    if (decodeMode != null) {
+      if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+        return PRODUCT_FORMATS;
+      }
+      if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+        return QR_CODE_FORMATS;
+      }
+      if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
+        return DATA_MATRIX_FORMATS;
+      }
+      if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+        return ONE_D_FORMATS;
+      }
+    }
+    return null;
+  }
+
+}

+ 112 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeHandler.java

@@ -0,0 +1,112 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.common.HybridBinarizer;
+import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.zxing.activity.CaptureActivity;
+import com.qingdaofushan.homecaresas.zxing.camera.CameraManager;
+import com.qingdaofushan.homecaresas.zxing.camera.PlanarYUVLuminanceSource;
+
+import java.util.Hashtable;
+
+
+final class DecodeHandler extends Handler {
+
+    private static final String TAG = DecodeHandler.class.getSimpleName();
+
+    private final CaptureActivity activity;
+    private final MultiFormatReader multiFormatReader;
+
+    DecodeHandler(CaptureActivity activity, Hashtable<DecodeHintType, Object> hints) {
+        multiFormatReader = new MultiFormatReader();
+        multiFormatReader.setHints(hints);
+        this.activity = activity;
+    }
+
+    @Override
+    public void handleMessage(Message message) {
+        switch (message.what) {
+            case R.id.decode:
+                //Log.d(TAG, "Got decode message");
+                decode((byte[]) message.obj, message.arg1, message.arg2);
+                break;
+            case R.id.quit:
+                Looper.myLooper().quit();
+                break;
+        }
+    }
+
+    /**
+     * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
+     * reuse the same reader objects from one decode to the next.
+     *
+     * @param data   The YUV preview frame.
+     * @param width  The width of the preview frame.
+     * @param height The height of the preview frame.
+     */
+    private void decode(byte[] data, int width, int height) {
+        long start = System.currentTimeMillis();
+        Result rawResult = null;
+
+        //modify here
+        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; // Here we are swapping, that's the difference to #11
+        width = height;
+        height = tmp;
+
+        PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
+        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+        try {
+            rawResult = multiFormatReader.decodeWithState(bitmap);
+        } catch (ReaderException re) {
+            // continue
+        } finally {
+            multiFormatReader.reset();
+        }
+
+        if (rawResult != null) {
+            long end = System.currentTimeMillis();
+            Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
+            Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
+            message.setData(bundle);
+            //Log.d(TAG, "Sending decode succeeded message...");
+            message.sendToTarget();
+        } else {
+            Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
+            message.sendToTarget();
+        }
+    }
+
+}

+ 86 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/DecodeThread.java

@@ -0,0 +1,86 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+import com.qingdaofushan.homecaresas.zxing.activity.CaptureActivity;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ * �����߳�
+ */
+final class DecodeThread extends Thread {
+
+    public static final String BARCODE_BITMAP = "barcode_bitmap";
+    private final CaptureActivity activity;
+    private final Hashtable<DecodeHintType, Object> hints;
+    private Handler handler;
+    private final CountDownLatch handlerInitLatch;
+
+    DecodeThread(CaptureActivity activity,
+                 Vector<BarcodeFormat> decodeFormats,
+                 String characterSet,
+                 ResultPointCallback resultPointCallback) {
+
+        this.activity = activity;
+        handlerInitLatch = new CountDownLatch(1);
+
+        hints = new Hashtable<DecodeHintType, Object>(3);
+
+        if (decodeFormats == null || decodeFormats.isEmpty()) {
+            decodeFormats = new Vector<BarcodeFormat>();
+            decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+            decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+            decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+        }
+
+        hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+
+        if (characterSet != null) {
+            hints.put(DecodeHintType.CHARACTER_SET, characterSet);
+        }
+
+        hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
+    }
+
+    Handler getHandler() {
+        try {
+            handlerInitLatch.await();
+        } catch (InterruptedException ie) {
+            // continue?
+        }
+        return handler;
+    }
+
+    @Override
+    public void run() {
+        Looper.prepare();
+        handler = new DecodeHandler(activity, hints);
+        handlerInitLatch.countDown();
+        Looper.loop();
+    }
+
+}

+ 47 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/FinishListener.java

@@ -0,0 +1,47 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+
+/**
+ * Simple listener used to exit the app in a few cases.
+ *
+ */
+public final class FinishListener
+    implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable {
+
+  private final Activity activityToFinish;
+
+  public FinishListener(Activity activityToFinish) {
+    this.activityToFinish = activityToFinish;
+  }
+
+  public void onCancel(DialogInterface dialogInterface) {
+    run();
+  }
+
+  public void onClick(DialogInterface dialogInterface, int i) {
+    run();
+  }
+
+  public void run() {
+    activityToFinish.finish();
+  }
+
+}

+ 71 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/InactivityTimer.java

@@ -0,0 +1,71 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.app.Activity;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Finishes an activity after a period of inactivity.
+ */
+public final class InactivityTimer {
+
+  private static final int INACTIVITY_DELAY_SECONDS = 5 * 60;
+
+  private final ScheduledExecutorService inactivityTimer =
+      Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
+  private final Activity activity;
+  private ScheduledFuture<?> inactivityFuture = null;
+
+  public InactivityTimer(Activity activity) {
+    this.activity = activity;
+    onActivity();
+  }
+
+  public void onActivity() {
+    cancel();
+    inactivityFuture = inactivityTimer.schedule(new FinishListener(activity),
+                                                INACTIVITY_DELAY_SECONDS,
+                                                TimeUnit.SECONDS);
+  }
+
+  private void cancel() {
+    if (inactivityFuture != null) {
+      inactivityFuture.cancel(true);
+      inactivityFuture = null;
+    }
+  }
+
+  public void shutdown() {
+    cancel();
+    inactivityTimer.shutdown();
+  }
+
+  private static final class DaemonThreadFactory implements ThreadFactory {
+    public Thread newThread(Runnable runnable) {
+      Thread thread = new Thread(runnable);
+      thread.setDaemon(true);
+      return thread;
+    }
+  }
+
+}

+ 190 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/Intents.java

@@ -0,0 +1,190 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.decoding;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ */
+public final class Intents {
+  private Intents() {
+  }
+
+  public static final class Scan {
+    /**
+     * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
+     * the results.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SCAN";
+
+    /**
+     * By default, sending Scan.ACTION will decode all barcodes that we understand. However it
+     * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with
+     * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}).
+     * Optional.
+     *
+     * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}.
+     * It is overridden by that setting.
+     */
+    public static final String MODE = "SCAN_MODE";
+
+    /**
+     * Comma-separated list of formats to scan for. The values must match the names of
+     * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}.
+     * Example: "EAN_13,EAN_8,QR_CODE"
+     *
+     * This overrides {@link #MODE}.
+     */
+    public static final String SCAN_FORMATS = "SCAN_FORMATS";
+
+    /**
+     * @see com.google.zxing.DecodeHintType#CHARACTER_SET
+     */
+    public static final String CHARACTER_SET = "CHARACTER_SET";
+
+    /**
+     * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
+     * prices, reviews, etc. for products.
+     */
+    public static final String PRODUCT_MODE = "PRODUCT_MODE";
+
+    /**
+     * Decode only 1D barcodes (currently UPC, EAN, Code 39, and Code 128).
+     */
+    public static final String ONE_D_MODE = "ONE_D_MODE";
+
+    /**
+     * Decode only QR codes.
+     */
+    public static final String QR_CODE_MODE = "QR_CODE_MODE";
+
+    /**
+     * Decode only Data Matrix codes.
+     */
+    public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
+
+    /**
+     * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which
+     * requested the scan via startSubActivity(). The barcodes contents can be retrieved with
+     * intent.getStringExtra(RESULT). If the user presses Back, the result code will be
+     * RESULT_CANCELED.
+     */
+    public static final String RESULT = "SCAN_RESULT";
+
+    /**
+     * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found.
+     * See Contents.Format for possible values.
+     */
+    public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
+
+    /**
+     * Setting this to false will not save scanned codes in the history.
+     */
+    public static final String SAVE_HISTORY = "SAVE_HISTORY";
+
+    private Scan() {
+    }
+  }
+
+  public static final class Encode {
+    /**
+     * Send this intent to encode a piece of data as a QR code and display it full screen, so
+     * that another person can scan the barcode from your screen.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.ENCODE";
+
+    /**
+     * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a
+     * Bundle, depending on the type and format specified. Non-QR Code formats should
+     * just use a String here. For QR Code, see Contents for details.
+     */
+    public static final String DATA = "ENCODE_DATA";
+
+    /**
+     * The type of data being supplied if the format is QR Code. Use
+     * Intent.putExtra(TYPE, type) with one of Contents.Type.
+     */
+    public static final String TYPE = "ENCODE_TYPE";
+    
+    /**
+     * The barcode format to be displayed. If this isn't specified or is blank, 
+     * it defaults to QR Code. Use Intent.putExtra(FORMAT, format), where
+     * format is one of Contents.Format. 
+     */
+    public static final String FORMAT = "ENCODE_FORMAT";
+
+    private Encode() {
+    }
+  }
+
+  public static final class SearchBookContents {
+    /**
+     * Use Google Book Search to search the contents of the book provided.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
+
+    /**
+     * The book to search, identified by ISBN number.
+     */
+    public static final String ISBN = "ISBN";
+
+    /**
+     * An optional field which is the text to search for.
+     */
+    public static final String QUERY = "QUERY";
+
+    private SearchBookContents() {
+    }
+  }
+
+  public static final class WifiConnect {
+	    /**
+	     * Internal intent used to trigger connection to a wi-fi network.
+	     */
+	    public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
+
+	    /**
+	     * The network to connect to, all the configuration provided here.
+	     */
+	    public static final String SSID = "SSID";
+
+	    /**
+	     * The network to connect to, all the configuration provided here.
+	     */
+	    public static final String TYPE = "TYPE";
+
+	    /**
+	     * The network to connect to, all the configuration provided here.
+	     */
+	    public static final String PASSWORD = "PASSWORD";
+
+	    private WifiConnect() {
+	    }
+	  }
+
+
+  public static final class Share {
+    /**
+     * Give the user a choice of items to encode as a barcode, then render it as a QR Code and
+     * display onscreen for a friend to scan with their phone.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SHARE";
+
+    private Share() {
+    }
+  }
+}

+ 104 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/decoding/RGBLuminanceSource.java

@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 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.qingdaofushan.homecaresas.zxing.decoding;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import com.google.zxing.LuminanceSource;
+
+import java.io.FileNotFoundException;
+
+/**
+ * This class is used to help decode images from files which arrive as RGB data
+ * from Android bitmaps. It does not support cropping or rotation.
+ * 
+ */
+public final class RGBLuminanceSource extends LuminanceSource {
+
+	private final byte[] luminances;
+
+	public RGBLuminanceSource(String path) throws FileNotFoundException {
+		this(loadBitmap(path));
+	}
+
+	public RGBLuminanceSource(Bitmap bitmap) {
+		super(bitmap.getWidth(), bitmap.getHeight());
+
+		int width = bitmap.getWidth();
+		int height = bitmap.getHeight();
+
+		int[] pixels = new int[width * height];
+		bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+		// In order to measure pure decoding speed, we convert the entire image
+		// to a greyscale array
+		// up front, which is the same as the Y channel of the
+		// YUVLuminanceSource in the real app.
+		luminances = new byte[width * height];
+		for (int y = 0; y < height; y++) {
+			int offset = y * width;
+			for (int x = 0; x < width; x++) {
+				int pixel = pixels[offset + x];
+				int r = (pixel >> 16) & 0xff;
+				int g = (pixel >> 8) & 0xff;
+				int b = pixel & 0xff;
+				if (r == g && g == b) {
+					// Image is already greyscale, so pick any channel.
+					luminances[offset + x] = (byte) r;
+				} else {
+					// Calculate luminance cheaply, favoring green.
+					luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
+				}
+			}
+		}
+	}
+
+
+
+
+	@Override
+	public byte[] getRow(int y, byte[] row) {
+		if (y < 0 || y >= getHeight()) {
+			throw new IllegalArgumentException("Requested row is outside the image: " + y);
+		}
+		int width = getWidth();
+		if (row == null || row.length < width) {
+			row = new byte[width];
+		}
+
+		System.arraycopy(luminances, y * width, row, 0, width);
+		return row;
+	}
+
+	// Since this class does not support cropping, the underlying byte array
+	// already contains
+	// exactly what the caller is asking for, so give it to them without a copy.
+	@Override
+	public byte[] getMatrix() {
+		return luminances;
+	}
+
+	private static Bitmap loadBitmap(String path) throws FileNotFoundException {
+		Bitmap bitmap = BitmapFactory.decodeFile(path);
+		if (bitmap == null) {
+			throw new FileNotFoundException("Couldn't open " + path);
+		}
+		return bitmap;
+	}
+
+}

+ 132 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/encoding/EncodingHandler.java

@@ -0,0 +1,132 @@
+package com.qingdaofushan.homecaresas.zxing.encoding;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * @author Ryan Tang
+ */
+public final class EncodingHandler {
+    private static final int BLACK = 0xff000000;
+
+    public static Bitmap createQRCode(String str, int widthAndHeight) throws WriterException {
+        Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
+        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
+        BitMatrix matrix = new MultiFormatWriter().encode(str,
+                BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight);
+        int width = matrix.getWidth();
+        int height = matrix.getHeight();
+        int[] pixels = new int[width * height];
+
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                if (matrix.get(x, y)) {
+                    pixels[y * width + x] = BLACK;
+                }
+            }
+        }
+        Bitmap bitmap = Bitmap.createBitmap(width, height,
+                Bitmap.Config.ARGB_8888);
+        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+        return bitmap;
+    }
+
+    /**
+     * 创建二维码
+     *
+     * @param content   content
+     * @param widthPix  widthPix
+     * @param heightPix heightPix
+     * @param logoBm    logoBm
+     * @return 二维码
+     */
+    public static Bitmap createQRCode(String content, int widthPix, int heightPix, Bitmap logoBm) {
+        try {
+            if (content == null || "".equals(content)) {
+                return null;
+            }
+            // 配置参数
+            Map<EncodeHintType, Object> hints = new HashMap<>();
+            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
+            // 容错级别
+            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+            // 图像数据转换,使用了矩阵转换
+            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix,
+                    heightPix, hints);
+            int[] pixels = new int[widthPix * heightPix];
+            // 下面这里按照二维码的算法,逐个生成二维码的图片,
+            // 两个for循环是图片横列扫描的结果
+            for (int y = 0; y < heightPix; y++) {
+                for (int x = 0; x < widthPix; x++) {
+                    if (bitMatrix.get(x, y)) {
+                        pixels[y * widthPix + x] = 0xff000000;
+                    } else {
+                        pixels[y * widthPix + x] = 0xffffffff;
+                    }
+                }
+            }
+            // 生成二维码图片的格式,使用ARGB_8888
+            Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
+            bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
+            if (logoBm != null) {
+                bitmap = addLogo(bitmap, logoBm);
+            }
+            //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
+            return bitmap;
+        } catch (WriterException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 在二维码中间添加Logo图案
+     */
+    private static Bitmap addLogo(Bitmap src, Bitmap logo) {
+        if (src == null) {
+            return null;
+        }
+        if (logo == null) {
+            return src;
+        }
+        //获取图片的宽高
+        int srcWidth = src.getWidth();
+        int srcHeight = src.getHeight();
+        int logoWidth = logo.getWidth();
+        int logoHeight = logo.getHeight();
+        if (srcWidth == 0 || srcHeight == 0) {
+            return null;
+        }
+        if (logoWidth == 0 || logoHeight == 0) {
+            return src;
+        }
+        //logo大小为二维码整体大小的1/5
+        float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
+        Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+        try {
+            Canvas canvas = new Canvas(bitmap);
+            canvas.drawBitmap(src, 0, 0, null);
+            canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
+            canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
+            //Canvas.ALL_SAVE_FLAG
+            canvas.save();
+            canvas.restore();
+        } catch (Exception e) {
+            bitmap = null;
+            e.getStackTrace();
+        }
+        return bitmap;
+    }
+}

+ 12 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/Constant.java

@@ -0,0 +1,12 @@
+package com.qingdaofushan.homecaresas.zxing.view;
+
+/**
+ * 常量
+ */
+public class Constant {
+    // request参数
+    public static final int REQ_QR_CODE = 11002; // // 打开扫描界面请求码
+    public static final int REQ_PERM_CAMERA = 11003; // 打开摄像头
+
+    public static final String INTENT_EXTRA_KEY_QR_SCAN = "qr_scan_result";
+}

+ 92 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/UriUtil.java

@@ -0,0 +1,92 @@
+package com.qingdaofushan.homecaresas.zxing.view;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+/**
+ * Uri路径工具
+ */
+
+public class UriUtil {
+    /**
+     * 根据图片的Uri获取图片的绝对路径(适配多种API)
+     *
+     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null
+     */
+    public static String getRealPathFromUri(Context context, Uri uri) {
+        int sdkVersion = Build.VERSION.SDK_INT;
+        if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri);
+        if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri);
+        else return getRealPathFromUri_AboveApi19(context, uri);
+    }
+
+    /**
+     * 适配api19以上,根据uri获取图片的绝对路径
+     */
+    @TargetApi(Build.VERSION_CODES.KITKAT)
+    private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
+        String filePath = null;
+        String wholeID = DocumentsContract.getDocumentId(uri);
+
+        // 使用':'分割
+        String[] ids = wholeID.split(":");
+        String id = null;
+        if (ids == null) {
+            return null;
+        }
+        if (ids.length > 1) {
+            id = ids[1];
+        } else {
+            id = ids[0];
+        }
+
+        String[] projection = {MediaStore.Images.Media.DATA};
+        String selection = MediaStore.Images.Media._ID + "=?";
+        String[] selectionArgs = {id};
+
+        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,//
+                projection, selection, selectionArgs, null);
+        int columnIndex = cursor.getColumnIndex(projection[0]);
+        if (cursor.moveToFirst()) filePath = cursor.getString(columnIndex);
+        cursor.close();
+        return filePath;
+    }
+
+    /**
+     * 适配api11-api18,根据uri获取图片的绝对路径
+     */
+    private static String getRealPathFromUri_Api11To18(Context context, Uri uri) {
+        String filePath = null;
+        String[] projection = {MediaStore.Images.Media.DATA};
+        CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null);
+        Cursor cursor = loader.loadInBackground();
+
+        if (cursor != null) {
+            cursor.moveToFirst();
+            filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+            cursor.close();
+        }
+        return filePath;
+    }
+
+    /**
+     * 适配api11以下(不包括api11),根据uri获取图片的绝对路径
+     */
+    private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) {
+        String filePath = null;
+        String[] projection = {MediaStore.Images.Media.DATA};
+        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+        if (cursor != null) {
+            cursor.moveToFirst();
+            filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+            cursor.close();
+        }
+        return filePath;
+    }
+}

+ 33 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/ViewfinderResultPointCallback.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009 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.qingdaofushan.homecaresas.zxing.view;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+public final class ViewfinderResultPointCallback implements ResultPointCallback {
+  private final ViewfinderView viewfinderView;
+
+  public ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
+    this.viewfinderView = viewfinderView;
+  }
+
+  public void foundPossibleResultPoint(ResultPoint point) {
+    viewfinderView.addPossibleResultPoint(point);
+  }
+
+}

+ 285 - 0
app/src/main/java/com/qingdaofushan/homecaresas/zxing/view/ViewfinderView.java

@@ -0,0 +1,285 @@
+/*
+ * 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.qingdaofushan.homecaresas.zxing.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.google.zxing.ResultPoint;
+import com.qingdaofushan.homecaresas.R;
+import com.qingdaofushan.homecaresas.zxing.camera.CameraManager;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+
+/**
+ * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
+ * transparency outside it, as well as the laser scanner animation and result points.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ViewfinderView extends View {
+
+    private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
+    private static final long ANIMATION_DELAY = 10L;
+    private static final int OPAQUE = 0xFF;
+    private static final int CORNER_RECT_WIDTH = 8;  //扫描区边角的宽
+    private static final int CORNER_RECT_HEIGHT = 40; //扫描区边角的高
+    private static final int SCANNER_LINE_MOVE_DISTANCE = 5;  //扫描线移动距离
+    private static final int SCANNER_LINE_HEIGHT = 10;  //扫描线宽度
+
+    private final Paint paint;
+    private Bitmap resultBitmap;
+    //模糊区域颜色
+    private final int maskColor;
+    private final int resultColor;
+    //扫描区域边框颜色
+    private final int frameColor;
+    //扫描线颜色
+    private final int laserColor;
+    //四角颜色
+    private final int cornerColor;
+    //扫描点的颜色
+    private final int resultPointColor;
+    private int scannerAlpha;
+    //扫描区域提示文本
+    private final String labelText;
+    //扫描区域提示文本颜色
+    private final int labelTextColor;
+    private final float labelTextSize;
+
+    public static int scannerStart = 0;
+    public static int scannerEnd = 0;
+
+    private Collection<ResultPoint> possibleResultPoints;
+    private Collection<ResultPoint> lastPossibleResultPoints;
+
+    // This constructor is used when the class is built from an XML resource.
+    public ViewfinderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        //初始化自定义属性信息
+        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
+        laserColor = array.getColor(R.styleable.ViewfinderView_laser_color, 0x00FF00);
+        cornerColor = array.getColor(R.styleable.ViewfinderView_corner_color, 0x00FF00);
+        frameColor = array.getColor(R.styleable.ViewfinderView_frame_color, 0xFFFFFF);
+        resultPointColor = array.getColor(R.styleable.ViewfinderView_result_point_color, 0xC0FFFF00);
+        maskColor = array.getColor(R.styleable.ViewfinderView_mask_color, 0x60000000);
+        resultColor = array.getColor(R.styleable.ViewfinderView_result_color, 0xB0000000);
+        labelTextColor = array.getColor(R.styleable.ViewfinderView_label_text_color, 0x90FFFFFF);
+        labelText = array.getString(R.styleable.ViewfinderView_label_text);
+        labelTextSize = array.getFloat(R.styleable.ViewfinderView_label_text_size, 36f);
+
+        // Initialize these once for performance rather than calling them every time in onDraw().
+        paint = new Paint();
+        paint.setAntiAlias(true);
+        scannerAlpha = 0;
+        possibleResultPoints = new HashSet<ResultPoint>(5);
+
+
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        Rect frame = CameraManager.get().getFramingRect();
+        if (frame == null) {
+            return;
+        }
+        if (scannerStart == 0 || scannerEnd == 0) {
+            scannerStart = frame.top;
+            scannerEnd = frame.bottom;
+        }
+
+        int width = canvas.getWidth();
+        int height = canvas.getHeight();
+        // Draw the exterior (i.e. outside the framing rect) darkened
+        drawExterior(canvas, frame, width, height);
+
+
+        if (resultBitmap != null) {
+            // Draw the opaque result bitmap over the scanning rectangle
+            paint.setAlpha(OPAQUE);
+            canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
+        } else {
+            // Draw a two pixel solid black border inside the framing rect
+            drawFrame(canvas, frame);
+            // 绘制边角
+            drawCorner(canvas, frame);
+            //绘制提示信息
+            drawTextInfo(canvas, frame);
+            // Draw a red "laser scanner" line through the middle to show decoding is active
+            drawLaserScanner(canvas, frame);
+
+            Collection<ResultPoint> currentPossible = possibleResultPoints;
+            Collection<ResultPoint> currentLast = lastPossibleResultPoints;
+            if (currentPossible.isEmpty()) {
+                lastPossibleResultPoints = null;
+            } else {
+                possibleResultPoints = new HashSet<ResultPoint>(5);
+                lastPossibleResultPoints = currentPossible;
+                paint.setAlpha(OPAQUE);
+                paint.setColor(resultPointColor);
+                for (ResultPoint point : currentPossible) {
+                    canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
+                }
+            }
+            if (currentLast != null) {
+                paint.setAlpha(OPAQUE / 2);
+                paint.setColor(resultPointColor);
+                for (ResultPoint point : currentLast) {
+                    canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
+                }
+            }
+
+            // Request another update at the animation interval, but only repaint the laser line,
+            // not the entire viewfinder mask.
+            //指定重绘区域,该方法会在子线程中执行
+            postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
+        }
+    }
+
+    //绘制文本
+    private void drawTextInfo(Canvas canvas, Rect frame) {
+        paint.setColor(labelTextColor);
+        paint.setTextSize(labelTextSize);
+        paint.setTextAlign(Paint.Align.CENTER);
+        canvas.drawText(labelText, frame.left + frame.width() / 2, frame.bottom + CORNER_RECT_HEIGHT * 1.5f, paint);
+    }
+
+
+    //绘制边角
+    private void drawCorner(Canvas canvas, Rect frame) {
+        paint.setColor(cornerColor);
+        //左上
+        canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_WIDTH, frame.top + CORNER_RECT_HEIGHT, paint);
+        canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_HEIGHT, frame.top + CORNER_RECT_WIDTH, paint);
+        //右上
+        canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.top, frame.right, frame.top + CORNER_RECT_HEIGHT, paint);
+        canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.top, frame.right, frame.top + CORNER_RECT_WIDTH, paint);
+        //左下
+        canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_WIDTH, frame.left + CORNER_RECT_HEIGHT, frame.bottom, paint);
+        canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_HEIGHT, frame.left + CORNER_RECT_WIDTH, frame.bottom, paint);
+        //右下
+        canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.bottom - CORNER_RECT_HEIGHT, frame.right, frame.bottom, paint);
+        canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.bottom - CORNER_RECT_WIDTH, frame.right, frame.bottom, paint);
+    }
+
+    //绘制扫描线
+    private void drawLaserScanner(Canvas canvas, Rect frame) {
+        paint.setColor(laserColor);
+        //扫描线闪烁效果
+//    paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
+//    scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
+//    int middle = frame.height() / 2 + frame.top;
+//    canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
+        //线性渐变
+        LinearGradient linearGradient = new LinearGradient(
+                frame.left, scannerStart,
+                frame.left, scannerStart + SCANNER_LINE_HEIGHT,
+                shadeColor(laserColor),
+                laserColor,
+                Shader.TileMode.MIRROR);
+
+        RadialGradient radialGradient = new RadialGradient(
+                (float) (frame.left + frame.width() / 2),
+                (float) (scannerStart + SCANNER_LINE_HEIGHT / 2),
+                360f,
+                laserColor,
+                shadeColor(laserColor),
+                Shader.TileMode.MIRROR);
+
+        SweepGradient sweepGradient = new SweepGradient(
+                (float) (frame.left + frame.width() / 2),
+                (float) (scannerStart + SCANNER_LINE_HEIGHT),
+                shadeColor(laserColor),
+                laserColor);
+
+        ComposeShader composeShader = new ComposeShader(radialGradient, linearGradient, PorterDuff.Mode.ADD);
+
+        paint.setShader(radialGradient);
+        if (scannerStart <= scannerEnd) {
+            //矩形
+//      canvas.drawRect(frame.left, scannerStart, frame.right, scannerStart + SCANNER_LINE_HEIGHT, paint);
+            //椭圆
+            RectF rectF = new RectF(frame.left + 2 * SCANNER_LINE_HEIGHT, scannerStart, frame.right - 2 * SCANNER_LINE_HEIGHT, scannerStart + SCANNER_LINE_HEIGHT);
+            canvas.drawOval(rectF, paint);
+            scannerStart += SCANNER_LINE_MOVE_DISTANCE;
+        } else {
+            scannerStart = frame.top;
+        }
+        paint.setShader(null);
+    }
+
+    //处理颜色模糊
+    public int shadeColor(int color) {
+        String hax = Integer.toHexString(color);
+        String result = "20" + hax.substring(2);
+        return Integer.valueOf(result, 16);
+    }
+
+    // 绘制扫描区边框 Draw a two pixel solid black border inside the framing rect
+    private void drawFrame(Canvas canvas, Rect frame) {
+        paint.setColor(frameColor);
+        canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
+        canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
+        canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
+        canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
+    }
+
+    // 绘制模糊区域 Draw the exterior (i.e. outside the framing rect) darkened
+    private void drawExterior(Canvas canvas, Rect frame, int width, int height) {
+        paint.setColor(resultBitmap != null ? resultColor : maskColor);
+        canvas.drawRect(0, 0, width, frame.top, paint);
+        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
+        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
+        canvas.drawRect(0, frame.bottom + 1, width, height, paint);
+    }
+
+    public void drawViewfinder() {
+        resultBitmap = null;
+        invalidate();
+    }
+
+    /**
+     * Draw a bitmap with the result points highlighted instead of the live scanning display.
+     *
+     * @param barcode An image of the decoded barcode.
+     */
+    public void drawResultBitmap(Bitmap barcode) {
+        resultBitmap = barcode;
+        invalidate();
+    }
+
+    public void addPossibleResultPoint(ResultPoint point) {
+        possibleResultPoints.add(point);
+    }
+
+}

BIN
app/src/main/res/drawable/flash_off.png


BIN
app/src/main/res/drawable/flash_on.png


+ 8 - 0
app/src/main/res/layout/activity_cam.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</RelativeLayout>

+ 39 - 4
app/src/main/res/layout/activity_live.xml

@@ -9,14 +9,49 @@
     <me.lake.librestreaming.ws.StreamLiveCameraView
         android:id="@+id/stream_previewView"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true">
+
+        <RelativeLayout
+            android:id="@+id/rl_suspend"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+
+            <ImageView
+                android:layout_width="@dimen/dp_50"
+                android:layout_height="@dimen/dp_50"
+                android:layout_centerInParent="true"
+                android:src="@mipmap/icon_zanting" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/rl_stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+
+            <ImageView
+                android:layout_width="@dimen/dp_50"
+                android:layout_height="@dimen/dp_50"
+                android:layout_centerInParent="true"
+                android:src="@mipmap/btn_end_call" />
+        </RelativeLayout>
+    </LinearLayout>
+
+
     <TextView
         android:id="@+id/tv_stop"
         android:layout_width="match_parent"
         android:layout_height="@dimen/dp_40"
-        android:textSize="@dimen/sp_20"
-        android:gravity="center"
         android:layout_alignParentBottom="true"
         android:background="#dddddd"
-        android:text="结束服务"/>
+        android:gravity="center"
+        android:text="结束服务"
+        android:textSize="@dimen/sp_20"
+        android:visibility="gone" />
 </RelativeLayout>

+ 44 - 0
app/src/main/res/layout/activity_scanner.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout 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"
+    android:orientation="vertical">
+
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <SurfaceView
+            android:id="@+id/scanner_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+
+        <com.qingdaofushan.homecaresas.zxing.view.ViewfinderView
+            android:id="@+id/viewfinder_content"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            app:corner_color="@color/corner_color"
+            app:frame_color="@color/viewfinder_frame"
+            app:label_text="将二维码放入框中,即可自动扫描"
+            app:label_text_color="#FFb2b2b2"
+            app:laser_color="@color/laser_color"
+            app:mask_color="@color/viewfinder_mask"
+            app:result_color="@color/result_view"
+            app:result_point_color="@color/result_point_color" />
+
+        <ImageButton
+            android:id="@+id/btn_flash"
+            android:layout_width="40dip"
+            android:layout_height="40dip"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginBottom="30dip"
+            android:background="?attr/selectableItemBackground"
+            android:padding="6dip"
+            android:scaleType="centerInside"
+            android:src="@drawable/flash_off" />
+    </FrameLayout>
+
+</RelativeLayout>

+ 0 - 2
app/src/main/res/layout/layout_download_dialog.xml

@@ -75,7 +75,6 @@
                 android:paddingTop="@dimen/dp_16"
                 android:paddingBottom="@dimen/dp_16"
                 android:singleLine="true"
-                android:textColor="#6200EE"
                 android:textSize="@dimen/sp_15"
                 android:layout_marginLeft="@dimen/dp_10" />
 
@@ -96,7 +95,6 @@
                 android:paddingTop="@dimen/dp_16"
                 android:paddingBottom="@dimen/dp_16"
                 android:singleLine="true"
-                android:textColor="#6200EE"
                 android:textSize="@dimen/sp_15" />
         </LinearLayout>
 

BIN
app/src/main/res/mipmap-xhdpi/btn_end_call.png


BIN
app/src/main/res/mipmap-xhdpi/icon_zanting.png


BIN
app/src/main/res/raw/beep.ogg


+ 24 - 0
app/src/main/res/values-sw432dp/attrs.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+
+    <!--corner_color 边角颜色,
+        scanner_color 扫描线颜色
+        possible_result_color 扫描点颜色
+        frame_color 扫描框边线颜色
+        mask_color 模糊区域颜色
+        label_text  框上方提示
+    -->
+    <declare-styleable name="ViewfinderView">
+        <attr name="corner_color" format="color"/>
+        <attr name="laser_color" format="color"/>
+        <attr name="frame_color" format="color" />
+        <attr name="mask_color" format="color" />
+        <attr name="result_point_color" format="color"/>
+        <attr name="result_color" format="color" />
+        <attr name="label_text_color" format="color"/>
+        <attr name="label_text" format="string"/>
+        <attr name="label_text_size" format="float"/>
+    </declare-styleable>
+
+</resources>

+ 19 - 0
app/src/main/res/values-sw432dp/ids.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item name="fab_expand_menu_button" type="id" />
+    <item name="fab_label" type="id" />
+
+    <!-- Messages IDs -->
+    <item type="id" name="auto_focus"/>
+    <item type="id" name="decode"/>
+    <item type="id" name="decode_failed"/>
+    <item type="id" name="decode_succeeded"/>
+    <item type="id" name="encode_failed"/>
+    <item type="id" name="encode_succeeded"/>
+    <item type="id" name="launch_product_query"/>
+    <item type="id" name="quit"/>
+    <item type="id" name="restart_preview"/>
+    <item type="id" name="return_scan_result"/>
+    <item type="id" name="search_book_contents_failed"/>
+    <item type="id" name="search_book_contents_succeeded"/>
+</resources>

+ 8 - 0
app/src/main/res/values/colors.xml

@@ -7,4 +7,12 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+    <!-- QRCode scanner color begin -->
+    <color name="viewfinder_mask">#60000000</color>
+    <color name="result_view">#B0000000</color>
+    <color name="viewfinder_frame">#90FFFFFF</color>
+    <color name="result_point_color">#C0FFFF00</color>
+    <color name="laser_color">#0F0</color>
+    <color name="corner_color">#00FF00</color>
+    <!-- QRCode scanner color end -->
 </resources>

+ 0 - 17
app/src/test/java/com/qingdaofushan/homecaresas/ExampleUnitTest.java

@@ -1,17 +0,0 @@
-package com.qingdaofushan.homecaresas;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
-    @Test
-    public void addition_isCorrect() {
-        assertEquals(4, 2 + 2);
-    }
-}

+ 2 - 2
libWSLive/build.gradle

@@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
 
 
 android {
-    compileSdkVersion 26
+    compileSdkVersion 29
     buildToolsVersion "26.0.3"
 
     defaultConfig {
         minSdkVersion 18
-        targetSdkVersion 31
+        targetSdkVersion 29
     }
 
     lintOptions {