diff --git a/app/build.gradle b/app/build.gradle
index 24f9be7..77aa3ff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -50,5 +50,5 @@ dependencies {
implementation deps.easypermissions
- implementation project(':lib')
+ implementation project(':zxing-lite')
}
diff --git a/app/src/main/java/com/king/zxing/app/MainActivity.java b/app/src/main/java/com/king/zxing/app/MainActivity.java
index 91f2516..3328982 100644
--- a/app/src/main/java/com/king/zxing/app/MainActivity.java
+++ b/app/src/main/java/com/king/zxing/app/MainActivity.java
@@ -18,21 +18,20 @@ package com.king.zxing.app;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.king.zxing.CameraScan;
import com.king.zxing.CaptureActivity;
-import com.king.zxing.app.util.UriUtils;
import com.king.zxing.util.CodeUtils;
import com.king.zxing.util.LogUtils;
+import java.io.IOException;
import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
@@ -50,9 +49,9 @@ import pub.devrel.easypermissions.EasyPermissions;
*
* 2、通过继承CaptureActivity或者CaptureFragment并自定义布局。(适用于大多场景,并无需关心扫码相关逻辑,自定义布局时需覆写getLayoutId方法)
*
- * 3、在你项目的Activity或者Fragment中创建一个CaptureHelper并在相应的生命周期中调用CaptureHelper的周期。(适用于想在扫码界面写交互逻辑,又因为项目架构或其它原因,无法直接或间接继承CaptureActivity或CaptureFragment时使用)
+ * 3、在你项目的Activity或者Fragment中实例化一个CameraScan即可。(适用于想在扫码界面写交互逻辑,又因为项目架构或其它原因,无法直接或间接继承CaptureActivity或CaptureFragment时使用)
*
- * 4、参照CaptureHelper写一个自定义的扫码帮助类,其它步骤同方式3。(扩展高级用法,谨慎使用)
+ * 4、继承CameraScan自己实现一个,可参照默认实现类DefaultCameraScan,其它步骤同方式3。(扩展高级用法,谨慎使用)
*
*/
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
@@ -109,26 +108,28 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
}
private void parsePhoto(Intent data){
- final String path = UriUtils.getImagePath(this,data);
- LogUtils.d("path:" + path);
- if(TextUtils.isEmpty(path)){
- return;
- }
- //异步解析
- asyncThread(new Runnable() {
- @Override
- public void run() {
- final String result = CodeUtils.parseCode(path);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Log.d("Jenly","result:" + result);
- Toast.makeText(getContext(),result,Toast.LENGTH_SHORT).show();
- }
+
+// final String path = UriUtils.getImagePath(this,data);
+// LogUtils.d("path:" + path);
+// if(TextUtils.isEmpty(path)){
+// return;
+// }
+
+ try {
+ Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),data.getData());
+ //异步解析
+ asyncThread(() -> {
+ final String result = CodeUtils.parseCode(bitmap);
+ runOnUiThread(() -> {
+ LogUtils.d("result:" + result);
+ Toast.makeText(getContext(),result,Toast.LENGTH_SHORT).show();
});
- }
- });
+ });
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
diff --git a/app/src/main/java/com/king/zxing/app/util/UriUtils.java b/app/src/main/java/com/king/zxing/app/util/UriUtils.java
index a000452..93a3ea6 100644
--- a/app/src/main/java/com/king/zxing/app/util/UriUtils.java
+++ b/app/src/main/java/com/king/zxing/app/util/UriUtils.java
@@ -10,6 +10,8 @@ import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
+import com.king.zxing.util.LogUtils;
+
/**
* @author Jenly Jenly
*/
@@ -28,7 +30,7 @@ public final class UriUtils {
//获取系統版本
int currentapiVersion = Build.VERSION.SDK_INT;
if(currentapiVersion> Build.VERSION_CODES.KITKAT){
- Log.d("uri=intent.getData :", "" + uri);
+ LogUtils.d("uri=intent.getData :" + uri);
if (DocumentsContract.isDocumentUri(context, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
Log.d("getDocumentId(uri) :", "" + docId);
diff --git a/settings.gradle b/settings.gradle
index 85e82b5..15445da 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':lib'
+include ':app', ':zxing-lite'
diff --git a/lib/.gitignore b/zxing-lite/.gitignore
similarity index 87%
rename from lib/.gitignore
rename to zxing-lite/.gitignore
index 3543521..796b96d 100644
--- a/lib/.gitignore
+++ b/zxing-lite/.gitignore
@@ -1 +1 @@
-/build
+/build
diff --git a/lib/bintray.gradle b/zxing-lite/bintray.gradle
similarity index 97%
rename from lib/bintray.gradle
rename to zxing-lite/bintray.gradle
index 779be5e..1719f79 100644
--- a/lib/bintray.gradle
+++ b/zxing-lite/bintray.gradle
@@ -1,12 +1,12 @@
-apply plugin: 'com.novoda.bintray-release'
-
-//添加
-publish {
- userOrg = 'jenly'//bintray.com用户名
- groupId = 'com.king.zxing'//jcenter上的路径
- artifactId = 'zxing-lite'//项目名称
- publishVersion = app_version.versionName//版本号
- desc = 'ZXingLite for Android'//描述
- website = 'https://github.com/jenly1314/ZXingLite'//网站
-
+apply plugin: 'com.novoda.bintray-release'
+
+//添加
+publish {
+ userOrg = 'jenly'//bintray.com用户名
+ groupId = 'com.king.zxing'//jcenter上的路径
+ artifactId = 'zxing-lite'//项目名称
+ publishVersion = app_version.versionName//版本号
+ desc = 'ZXingLite for Android'//描述
+ website = 'https://github.com/jenly1314/ZXingLite'//网站
+
}
\ No newline at end of file
diff --git a/lib/build.gradle b/zxing-lite/build.gradle
similarity index 96%
rename from lib/build.gradle
rename to zxing-lite/build.gradle
index 35ff9d9..90ee12c 100644
--- a/lib/build.gradle
+++ b/zxing-lite/build.gradle
@@ -1,51 +1,51 @@
-apply plugin: 'com.android.library'
-apply from: 'bintray.gradle'
-
-android {
- compileSdkVersion build_versions.compileSdk
- buildToolsVersion build_versions.buildTools
- defaultConfig {
- minSdkVersion build_versions.minSdk
- targetSdkVersion build_versions.targetSdk
- versionCode app_version.versionCode
- versionName app_version.versionName
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-
- lintOptions {
- abortOnError false
- warning 'InvalidPackage'
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-}
-
-//task javadoc(type: Javadoc) {
-// source = android.sourceSets.main.java.srcDirs
-// classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-//}
-
-dependencies {
- implementation fileTree(include: ['*.jar'], dir: 'libs')
- testImplementation deps.test.junit
- androidTestImplementation deps.test.runner
- androidTestImplementation deps.test.espresso
-
- compileOnly deps.androidx.appcompat
- api deps.zxing
- api deps.camera_core
- api deps.camera_camera2
- api deps.camera_lifecycle
- api deps.camera_view
-}
+apply plugin: 'com.android.library'
+apply from: 'bintray.gradle'
+
+android {
+ compileSdkVersion build_versions.compileSdk
+ buildToolsVersion build_versions.buildTools
+ defaultConfig {
+ minSdkVersion build_versions.minSdk
+ targetSdkVersion build_versions.targetSdk
+ versionCode app_version.versionCode
+ versionName app_version.versionName
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ lintOptions {
+ abortOnError false
+ warning 'InvalidPackage'
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+//task javadoc(type: Javadoc) {
+// source = android.sourceSets.main.java.srcDirs
+// classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+//}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ testImplementation deps.test.junit
+ androidTestImplementation deps.test.runner
+ androidTestImplementation deps.test.espresso
+
+ compileOnly deps.androidx.appcompat
+ api deps.zxing
+ api deps.camera_core
+ api deps.camera_camera2
+ api deps.camera_lifecycle
+ api deps.camera_view
+}
diff --git a/zxing-lite/gradle.properties b/zxing-lite/gradle.properties
new file mode 100644
index 0000000..60e9686
--- /dev/null
+++ b/zxing-lite/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=ZXingLite
+POM_ARTIFACT_ID=zxing-lite
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lib/proguard-rules.pro b/zxing-lite/proguard-rules.pro
similarity index 96%
rename from lib/proguard-rules.pro
rename to zxing-lite/proguard-rules.pro
index e786d4a..b9139f6 100644
--- a/lib/proguard-rules.pro
+++ b/zxing-lite/proguard-rules.pro
@@ -1,34 +1,34 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
-
-#ZXingLite
--dontwarn com.king.zxing.**
--keep class com.king.zxing.**{ *;}
--keepattributes InnerClasses
--keepclassmembers enum * {
- public static **[] values();
- public static ** valueOf(java.lang.String);
-}
-
-#ZXing
--dontwarn com.google.zxing.**
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+#ZXingLite
+-dontwarn com.king.zxing.**
+-keep class com.king.zxing.**{ *;}
+-keepattributes InnerClasses
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+#ZXing
+-dontwarn com.google.zxing.**
-keep class com.google.zxing.**{ *;}
\ No newline at end of file
diff --git a/lib/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java b/zxing-lite/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java
similarity index 96%
rename from lib/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java
rename to zxing-lite/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java
index cdd5ac9..0f6cc11 100644
--- a/lib/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java
+++ b/zxing-lite/src/androidTest/java/com/king/zxing/ExampleInstrumentedTest.java
@@ -1,27 +1,27 @@
-package com.king.zxing;
-
-import android.content.Context;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("com.king.zxing.test", appContext.getPackageName());
- }
-}
+package com.king.zxing;
+
+import android.content.Context;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.king.zxing.test", appContext.getPackageName());
+ }
+}
diff --git a/lib/src/main/AndroidManifest.xml b/zxing-lite/src/main/AndroidManifest.xml
similarity index 97%
rename from lib/src/main/AndroidManifest.xml
rename to zxing-lite/src/main/AndroidManifest.xml
index 1aa341d..fe7a4d3 100644
--- a/lib/src/main/AndroidManifest.xml
+++ b/zxing-lite/src/main/AndroidManifest.xml
@@ -1,15 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/src/main/java/com/king/zxing/CameraConfig.java b/zxing-lite/src/main/java/com/king/zxing/CameraConfig.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/CameraConfig.java
rename to zxing-lite/src/main/java/com/king/zxing/CameraConfig.java
diff --git a/lib/src/main/java/com/king/zxing/CameraScan.java b/zxing-lite/src/main/java/com/king/zxing/CameraScan.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/CameraScan.java
rename to zxing-lite/src/main/java/com/king/zxing/CameraScan.java
diff --git a/lib/src/main/java/com/king/zxing/CaptureActivity.java b/zxing-lite/src/main/java/com/king/zxing/CaptureActivity.java
similarity index 96%
rename from lib/src/main/java/com/king/zxing/CaptureActivity.java
rename to zxing-lite/src/main/java/com/king/zxing/CaptureActivity.java
index 5f261ea..9213f61 100644
--- a/lib/src/main/java/com/king/zxing/CaptureActivity.java
+++ b/zxing-lite/src/main/java/com/king/zxing/CaptureActivity.java
@@ -1,214 +1,214 @@
-/*
- * Copyright (C) 2018 Jenly Yu
- *
- * 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.king.zxing;
-
-import android.Manifest;
-import android.os.Bundle;
-import android.view.View;
-
-import com.google.zxing.Result;
-import com.king.zxing.util.LogUtils;
-import com.king.zxing.util.PermissionUtils;
-
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.camera.view.PreviewView;
-
-/**
- * @author Jenly
- */
-public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback{
-
- private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86;
-
- protected PreviewView previewView;
- protected ViewfinderView viewfinderView;
- protected View ivFlashlight;
-
- private CameraScan mCameraScan;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- int layoutId = getLayoutId();
- if(isContentView(layoutId)){
- setContentView(layoutId);
- }
- initUI();
- }
-
- /**
- * 初始化
- */
- public void initUI(){
- previewView = findViewById(getPreviewViewId());
- int viewfinderViewId = getViewfinderViewId();
- if(viewfinderViewId != 0){
- viewfinderView = findViewById(viewfinderViewId);
- }
- int ivFlashlightId = getFlashlightId();
- if(ivFlashlightId != 0){
- ivFlashlight = findViewById(ivFlashlightId);
- if(ivFlashlight != null){
- ivFlashlight.setOnClickListener(v -> onClickFlashlight());
- }
- }
- initCameraScan();
- startCamera();
- }
-
- /**
- * 点击手电筒
- */
- protected void onClickFlashlight(){
- toggleTorchState();
- }
-
- /**
- * 初始化CameraScan
- */
- public void initCameraScan(){
- mCameraScan = new DefaultCameraScan(this,previewView);
- mCameraScan.setOnScanResultCallback(this);
- }
-
-
- /**
- * 启动相机预览
- */
- public void startCamera(){
- if(mCameraScan != null){
- if(PermissionUtils.checkPermission(this,Manifest.permission.CAMERA)){
- mCameraScan.startCamera();
- }else{
- LogUtils.d("checkPermissionResult != PERMISSION_GRANTED");
- PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE);
- }
- }
- }
-
-
- /**
- * 释放相机
- */
- private void releaseCamera(){
- if(mCameraScan != null){
- mCameraScan.release();
- }
- }
-
- /**
- * 切换闪光灯状态(开启/关闭)
- */
- protected void toggleTorchState(){
- if(mCameraScan != null){
- boolean isTorch = mCameraScan.isTorchEnabled();
- mCameraScan.enableTorch(!isTorch);
- if(ivFlashlight != null){
- ivFlashlight.setSelected(!isTorch);
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){
- requestCameraPermissionResult(permissions,grantResults);
- }
- }
-
- /**
- * 请求Camera权限回调结果
- * @param permissions
- * @param grantResults
- */
- public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){
- if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){
- startCamera();
- }else{
- finish();
- }
- }
-
- @Override
- protected void onDestroy() {
- releaseCamera();
- super.onDestroy();
- }
-
- /**
- * 返回true时会自动初始化{@link #setContentView(int)},返回为false是需自己去初始化{@link #setContentView(int)}
- * @param layoutId
- * @return 默认返回true
- */
- public boolean isContentView(@LayoutRes int layoutId){
- return true;
- }
-
- /**
- * 布局id
- * @return
- */
- public int getLayoutId(){
- return R.layout.zxl_capture;
- }
-
- /**
- * {@link #viewfinderView} 的 ID
- * @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0
- */
- public int getViewfinderViewId(){
- return R.id.viewfinderView;
- }
-
-
- /**
- * 预览界面{@link #previewView} 的ID
- * @return
- */
- public int getPreviewViewId(){
- return R.id.previewView;
- }
-
- /**
- * 获取 {@link #ivFlashlight} 的ID
- * @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
- */
- public int getFlashlightId(){
- return R.id.ivFlashlight;
- }
-
- /**
- * Get {@link CameraScan}
- * @return {@link #mCameraScan}
- */
- public CameraScan getCameraScan(){
- return mCameraScan;
- }
-
- /**
- * 接收扫码结果回调
- * @param result 扫码结果
- * @return 返回true表示拦截,将不自动执行后续逻辑,为false表示不拦截,默认不拦截
- */
- @Override
- public boolean onScanResultCallback(Result result) {
- return false;
- }
+/*
+ * Copyright (C) 2018 Jenly Yu
+ *
+ * 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.king.zxing;
+
+import android.Manifest;
+import android.os.Bundle;
+import android.view.View;
+
+import com.google.zxing.Result;
+import com.king.zxing.util.LogUtils;
+import com.king.zxing.util.PermissionUtils;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.camera.view.PreviewView;
+
+/**
+ * @author Jenly
+ */
+public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback{
+
+ private static final int CAMERA_PERMISSION_REQUEST_CODE = 0X86;
+
+ protected PreviewView previewView;
+ protected ViewfinderView viewfinderView;
+ protected View ivFlashlight;
+
+ private CameraScan mCameraScan;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int layoutId = getLayoutId();
+ if(isContentView(layoutId)){
+ setContentView(layoutId);
+ }
+ initUI();
+ }
+
+ /**
+ * 初始化
+ */
+ public void initUI(){
+ previewView = findViewById(getPreviewViewId());
+ int viewfinderViewId = getViewfinderViewId();
+ if(viewfinderViewId != 0){
+ viewfinderView = findViewById(viewfinderViewId);
+ }
+ int ivFlashlightId = getFlashlightId();
+ if(ivFlashlightId != 0){
+ ivFlashlight = findViewById(ivFlashlightId);
+ if(ivFlashlight != null){
+ ivFlashlight.setOnClickListener(v -> onClickFlashlight());
+ }
+ }
+ initCameraScan();
+ startCamera();
+ }
+
+ /**
+ * 点击手电筒
+ */
+ protected void onClickFlashlight(){
+ toggleTorchState();
+ }
+
+ /**
+ * 初始化CameraScan
+ */
+ public void initCameraScan(){
+ mCameraScan = new DefaultCameraScan(this,previewView);
+ mCameraScan.setOnScanResultCallback(this);
+ }
+
+
+ /**
+ * 启动相机预览
+ */
+ public void startCamera(){
+ if(mCameraScan != null){
+ if(PermissionUtils.checkPermission(this,Manifest.permission.CAMERA)){
+ mCameraScan.startCamera();
+ }else{
+ LogUtils.d("checkPermissionResult != PERMISSION_GRANTED");
+ PermissionUtils.requestPermission(this,Manifest.permission.CAMERA,CAMERA_PERMISSION_REQUEST_CODE);
+ }
+ }
+ }
+
+
+ /**
+ * 释放相机
+ */
+ private void releaseCamera(){
+ if(mCameraScan != null){
+ mCameraScan.release();
+ }
+ }
+
+ /**
+ * 切换闪光灯状态(开启/关闭)
+ */
+ protected void toggleTorchState(){
+ if(mCameraScan != null){
+ boolean isTorch = mCameraScan.isTorchEnabled();
+ mCameraScan.enableTorch(!isTorch);
+ if(ivFlashlight != null){
+ ivFlashlight.setSelected(!isTorch);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if(requestCode == CAMERA_PERMISSION_REQUEST_CODE){
+ requestCameraPermissionResult(permissions,grantResults);
+ }
+ }
+
+ /**
+ * 请求Camera权限回调结果
+ * @param permissions
+ * @param grantResults
+ */
+ public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults){
+ if(PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA,permissions,grantResults)){
+ startCamera();
+ }else{
+ finish();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ releaseCamera();
+ super.onDestroy();
+ }
+
+ /**
+ * 返回true时会自动初始化{@link #setContentView(int)},返回为false是需自己去初始化{@link #setContentView(int)}
+ * @param layoutId
+ * @return 默认返回true
+ */
+ public boolean isContentView(@LayoutRes int layoutId){
+ return true;
+ }
+
+ /**
+ * 布局id
+ * @return
+ */
+ public int getLayoutId(){
+ return R.layout.zxl_capture;
+ }
+
+ /**
+ * {@link #viewfinderView} 的 ID
+ * @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0
+ */
+ public int getViewfinderViewId(){
+ return R.id.viewfinderView;
+ }
+
+
+ /**
+ * 预览界面{@link #previewView} 的ID
+ * @return
+ */
+ public int getPreviewViewId(){
+ return R.id.previewView;
+ }
+
+ /**
+ * 获取 {@link #ivFlashlight} 的ID
+ * @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
+ */
+ public int getFlashlightId(){
+ return R.id.ivFlashlight;
+ }
+
+ /**
+ * Get {@link CameraScan}
+ * @return {@link #mCameraScan}
+ */
+ public CameraScan getCameraScan(){
+ return mCameraScan;
+ }
+
+ /**
+ * 接收扫码结果回调
+ * @param result 扫码结果
+ * @return 返回true表示拦截,将不自动执行后续逻辑,为false表示不拦截,默认不拦截
+ */
+ @Override
+ public boolean onScanResultCallback(Result result) {
+ return false;
+ }
}
\ No newline at end of file
diff --git a/lib/src/main/java/com/king/zxing/CaptureFragment.java b/zxing-lite/src/main/java/com/king/zxing/CaptureFragment.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/CaptureFragment.java
rename to zxing-lite/src/main/java/com/king/zxing/CaptureFragment.java
diff --git a/lib/src/main/java/com/king/zxing/DecodeConfig.java b/zxing-lite/src/main/java/com/king/zxing/DecodeConfig.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/DecodeConfig.java
rename to zxing-lite/src/main/java/com/king/zxing/DecodeConfig.java
diff --git a/lib/src/main/java/com/king/zxing/DecodeFormatManager.java b/zxing-lite/src/main/java/com/king/zxing/DecodeFormatManager.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/DecodeFormatManager.java
rename to zxing-lite/src/main/java/com/king/zxing/DecodeFormatManager.java
diff --git a/lib/src/main/java/com/king/zxing/DefaultCameraScan.java b/zxing-lite/src/main/java/com/king/zxing/DefaultCameraScan.java
similarity index 99%
rename from lib/src/main/java/com/king/zxing/DefaultCameraScan.java
rename to zxing-lite/src/main/java/com/king/zxing/DefaultCameraScan.java
index b75d461..37071ba 100644
--- a/lib/src/main/java/com/king/zxing/DefaultCameraScan.java
+++ b/zxing-lite/src/main/java/com/king/zxing/DefaultCameraScan.java
@@ -16,6 +16,8 @@ import com.google.zxing.ResultPoint;
import com.google.zxing.common.detector.MathUtils;
import com.king.zxing.analyze.Analyzer;
import com.king.zxing.analyze.MultiFormatAnalyzer;
+import com.king.zxing.manager.AmbientLightManager;
+import com.king.zxing.manager.BeepManager;
import com.king.zxing.util.LogUtils;
import java.util.concurrent.Executors;
diff --git a/lib/src/main/java/com/king/zxing/ICamera.java b/zxing-lite/src/main/java/com/king/zxing/ICamera.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/ICamera.java
rename to zxing-lite/src/main/java/com/king/zxing/ICamera.java
diff --git a/lib/src/main/java/com/king/zxing/ICameraControl.java b/zxing-lite/src/main/java/com/king/zxing/ICameraControl.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/ICameraControl.java
rename to zxing-lite/src/main/java/com/king/zxing/ICameraControl.java
diff --git a/lib/src/main/java/com/king/zxing/ViewfinderView.java b/zxing-lite/src/main/java/com/king/zxing/ViewfinderView.java
similarity index 97%
rename from lib/src/main/java/com/king/zxing/ViewfinderView.java
rename to zxing-lite/src/main/java/com/king/zxing/ViewfinderView.java
index db7e943..8dab4dc 100644
--- a/lib/src/main/java/com/king/zxing/ViewfinderView.java
+++ b/zxing-lite/src/main/java/com/king/zxing/ViewfinderView.java
@@ -1,580 +1,580 @@
-package com.king.zxing;
-/*
- * 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.
- */
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.View;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.ColorRes;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
-
-import static com.king.zxing.ViewfinderView.FrameGravity.*;
-
-/**
- * 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 class ViewfinderView extends View {
-
- private static final int CURRENT_POINT_OPACITY = 0xA0;
- private static final int MAX_RESULT_POINTS = 20;
- private static final int POINT_SIZE = 30;
-
- /**
- * 画笔
- */
- private Paint paint;
-
- /**
- * 文本画笔
- */
- private TextPaint textPaint;
- /**
- * 扫码框外面遮罩颜色
- */
- private int maskColor;
- /**
- * 扫描区域边框颜色
- */
- private int frameColor;
- /**
- * 扫描线颜色
- */
- private int laserColor;
- /**
- * 扫码框四角颜色
- */
- private int cornerColor;
-
- /**
- * 提示文本与扫码框的边距
- */
- private float labelTextPadding;
- /**
- * 提示文本的宽度
- */
- private int labelTextWidth;
- /**
- * 提示文本的位置
- */
- private TextLocation labelTextLocation;
- /**
- * 扫描区域提示文本
- */
- private String labelText;
- /**
- * 扫描区域提示文本颜色
- */
- private int labelTextColor;
- /**
- * 提示文本字体大小
- */
- private float labelTextSize;
-
- /**
- * 扫描线开始位置
- */
- public int scannerStart = 0;
- /**
- * 扫描线结束位置
- */
- public int scannerEnd = 0;
-
- /**
- * 扫码框宽
- */
- private int frameWidth;
- /**
- * 扫码框高
- */
- private int frameHeight;
- /**
- * 扫描激光线风格
- */
- private LaserStyle laserStyle;
-
- /**
- * 网格列数
- */
- private int gridColumn;
- /**
- * 网格高度
- */
- private int gridHeight;
-
- /**
- * 扫码框
- */
- private Rect frame;
-
- /**
- * 扫描区边角的宽
- */
- private int cornerRectWidth;
- /**
- * 扫描区边角的高
- */
- private int cornerRectHeight;
- /**
- * 扫描线每次移动距离
- */
- private int scannerLineMoveDistance;
- /**
- * 扫描线高度
- */
- private int scannerLineHeight;
-
- /**
- * 边框线宽度
- */
- private int frameLineWidth;
-
- /**
- * 扫描动画延迟间隔时间 默认20毫秒
- */
- private int scannerAnimationDelay;
-
- /**
- * 扫码框占比
- */
- private float frameRatio;
-
- /**
- * 扫码框内间距
- */
- private float framePaddingLeft;
- private float framePaddingTop;
- private float framePaddingRight;
- private float framePaddingBottom;
- /**
- * 扫码框对齐方式
- */
- private FrameGravity frameGravity;
-
-
- private Point point;
- private int pointColor;
- private int pointStrokeColor;
-
- private float pointRadius;
- private float pointStrokeRatio = 1.2f;
-
-
- public enum LaserStyle{
- NONE(0),LINE(1),GRID(2);
- private int mValue;
- LaserStyle(int value){
- mValue = value;
- }
-
- private static LaserStyle getFromInt(int value){
- for(LaserStyle style : LaserStyle.values()){
- if(style.mValue == value){
- return style;
- }
- }
- return LaserStyle.LINE;
- }
- }
-
- public enum TextLocation {
- TOP(0),BOTTOM(1);
-
- private int mValue;
-
- TextLocation(int value){
- mValue = value;
- }
-
- private static TextLocation getFromInt(int value){
- for(TextLocation location : TextLocation.values()){
- if(location.mValue == value){
- return location;
- }
- }
- return TextLocation.TOP;
- }
- }
-
-
- public enum FrameGravity {
- CENTER(0), LEFT(1), TOP(2), RIGHT(3), BOTTOM(4);
-
- private int mValue;
-
- FrameGravity(int value) {
- mValue = value;
- }
-
- private static FrameGravity getFromInt(int value) {
- for (FrameGravity gravity : values()) {
- if (gravity.mValue == value) {
- return gravity;
- }
- }
- return CENTER;
- }
- }
-
- public ViewfinderView(Context context) {
- this(context,null);
- }
-
- public ViewfinderView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs,0);
- }
-
- public ViewfinderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context,attrs);
- }
-
-
- private void init(Context context, AttributeSet attrs) {
- //初始化自定义属性信息
- TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
- maskColor = array.getColor(R.styleable.ViewfinderView_maskColor, ContextCompat.getColor(context,R.color.viewfinder_mask));
- frameColor = array.getColor(R.styleable.ViewfinderView_frameColor, ContextCompat.getColor(context,R.color.viewfinder_frame));
- cornerColor = array.getColor(R.styleable.ViewfinderView_cornerColor, ContextCompat.getColor(context,R.color.viewfinder_corner));
- laserColor = array.getColor(R.styleable.ViewfinderView_laserColor, ContextCompat.getColor(context,R.color.viewfinder_laser));
-
- labelText = array.getString(R.styleable.ViewfinderView_labelText);
- labelTextColor = array.getColor(R.styleable.ViewfinderView_labelTextColor, ContextCompat.getColor(context,R.color.viewfinder_text_color));
- labelTextSize = array.getDimension(R.styleable.ViewfinderView_labelTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,14f,getResources().getDisplayMetrics()));
- labelTextPadding = array.getDimension(R.styleable.ViewfinderView_labelTextPadding,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,24,getResources().getDisplayMetrics()));
- labelTextWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_labelTextWidth,0);
- labelTextLocation = TextLocation.getFromInt(array.getInt(R.styleable.ViewfinderView_labelTextLocation,0));
-
- frameWidth = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameWidth,0);
- frameHeight = array.getDimensionPixelSize(R.styleable.ViewfinderView_frameHeight,0);
-
- laserStyle = LaserStyle.getFromInt(array.getInt(R.styleable.ViewfinderView_laserStyle,LaserStyle.LINE.mValue));
- gridColumn = array.getInt(R.styleable.ViewfinderView_gridColumn,20);
- gridHeight = (int)array.getDimension(R.styleable.ViewfinderView_gridHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40,getResources().getDisplayMetrics()));
-
- cornerRectWidth = (int)array.getDimension(R.styleable.ViewfinderView_cornerRectWidth,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4,getResources().getDisplayMetrics()));
- cornerRectHeight = (int)array.getDimension(R.styleable.ViewfinderView_cornerRectHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,16,getResources().getDisplayMetrics()));
- scannerLineMoveDistance = (int)array.getDimension(R.styleable.ViewfinderView_scannerLineMoveDistance,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,2,getResources().getDisplayMetrics()));
- scannerLineHeight = (int)array.getDimension(R.styleable.ViewfinderView_scannerLineHeight,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics()));
- frameLineWidth = (int)array.getDimension(R.styleable.ViewfinderView_frameLineWidth,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics()));
- scannerAnimationDelay = array.getInteger(R.styleable.ViewfinderView_scannerAnimationDelay,20);
- frameRatio = array.getFloat(R.styleable.ViewfinderView_frameRatio,0.625f);
- framePaddingLeft = array.getDimension(R.styleable.ViewfinderView_framePaddingLeft,0);
- framePaddingTop = array.getDimension(R.styleable.ViewfinderView_framePaddingTop,0);
- framePaddingRight = array.getDimension(R.styleable.ViewfinderView_framePaddingRight,0);
- framePaddingBottom = array.getDimension(R.styleable.ViewfinderView_framePaddingBottom,0);
- frameGravity = FrameGravity.getFromInt(array.getInt(R.styleable.ViewfinderView_frameGravity, CENTER.mValue));
- array.recycle();
-
- pointColor = laserColor;
- pointStrokeColor = Color.WHITE;
-
- pointRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getResources().getDisplayMetrics());
- paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
-
- }
-
- private DisplayMetrics getDisplayMetrics(){
- return getResources().getDisplayMetrics();
- }
-
- public void setLabelText(String labelText) {
- this.labelText = labelText;
- }
-
- public void setLabelTextColor(@ColorInt int color) {
- this.labelTextColor = color;
- }
-
- public void setLabelTextColorResource(@ColorRes int id){
- this.labelTextColor = ContextCompat.getColor(getContext(),id);
- }
-
- public void setLabelTextSize(float textSize) {
- this.labelTextSize = textSize;
- }
-
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- initFrame(w,h);
- }
-
- private void initFrame(int width,int height){
-
- int size = (int)(Math.min(width,height) * frameRatio);
-
- if(frameWidth <= 0 || frameWidth > width){
- frameWidth = size;
- }
-
- if(frameHeight <= 0 || frameHeight > height){
- frameHeight = size;
- }
-
- if(labelTextWidth <= 0){
- labelTextWidth = width - getPaddingLeft() - getPaddingRight();
- }
-
- float leftOffsets = (width - frameWidth) / 2 + framePaddingLeft - framePaddingRight;
- float topOffsets = (height - frameHeight) / 2 + framePaddingTop - framePaddingBottom;
- switch (frameGravity){
- case LEFT:
- leftOffsets = framePaddingLeft;
- break;
- case TOP:
- topOffsets = framePaddingTop;
- break;
- case RIGHT:
- leftOffsets = width - frameWidth + framePaddingRight;
- break;
- case BOTTOM:
- topOffsets = height - frameHeight + framePaddingBottom;
- break;
- }
-
- frame = new Rect((int)leftOffsets, (int)topOffsets, (int)leftOffsets + frameWidth, (int)topOffsets + frameHeight);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
-
- if (frame == null) {
- return;
- }
-
- if(scannerStart == 0 || scannerEnd == 0) {
- scannerStart = frame.top;
- scannerEnd = frame.bottom - scannerLineHeight;
- }
-
- int width = canvas.getWidth();
- int height = canvas.getHeight();
-
- // 绘制模糊区域
- drawExterior(canvas,frame,width,height);
- // 绘制扫描动画
- drawLaserScanner(canvas,frame);
- // 绘制取景区域框
- drawFrame(canvas, frame);
- // 绘制取景区域边角
- drawCorner(canvas, frame);
- //绘制提示信息
- drawTextInfo(canvas, frame);
- // 间隔更新取景区域
- postInvalidateDelayed(scannerAnimationDelay, frame.left, frame.top, frame.right, frame.bottom);
- }
-
- /**
- * 绘制文本
- * @param canvas
- * @param frame
- */
- private void drawTextInfo(Canvas canvas, Rect frame) {
- if(!TextUtils.isEmpty(labelText)){
- textPaint.setColor(labelTextColor);
- textPaint.setTextSize(labelTextSize);
- textPaint.setTextAlign(Paint.Align.CENTER);
- StaticLayout staticLayout = new StaticLayout(labelText,textPaint,labelTextWidth, Layout.Alignment.ALIGN_NORMAL,1.2f,0.0f,true);
- if(labelTextLocation == TextLocation.BOTTOM){
- canvas.translate(frame.left + frame.width() / 2,frame.bottom + labelTextPadding);
- }else{
- canvas.translate(frame.left + frame.width() / 2,frame.top - labelTextPadding - staticLayout.getHeight());
- }
- staticLayout.draw(canvas);
- }
-
- }
-
- /**
- * 绘制边角
- * @param canvas
- * @param frame
- */
- private void drawCorner(Canvas canvas, Rect frame) {
- paint.setColor(cornerColor);
- //左上
- canvas.drawRect(frame.left, frame.top, frame.left + cornerRectWidth, frame.top + cornerRectHeight, paint);
- canvas.drawRect(frame.left, frame.top, frame.left + cornerRectHeight, frame.top + cornerRectWidth, paint);
- //右上
- canvas.drawRect(frame.right - cornerRectWidth, frame.top, frame.right, frame.top + cornerRectHeight, paint);
- canvas.drawRect(frame.right - cornerRectHeight, frame.top, frame.right, frame.top + cornerRectWidth, paint);
- //左下
- canvas.drawRect(frame.left, frame.bottom - cornerRectWidth, frame.left + cornerRectHeight, frame.bottom, paint);
- canvas.drawRect(frame.left, frame.bottom - cornerRectHeight, frame.left + cornerRectWidth, frame.bottom, paint);
- //右下
- canvas.drawRect(frame.right - cornerRectWidth, frame.bottom - cornerRectHeight, frame.right, frame.bottom, paint);
- canvas.drawRect(frame.right - cornerRectHeight, frame.bottom - cornerRectWidth, frame.right, frame.bottom, paint);
- }
-
- /**
- * 绘制激光扫描线
- * @param canvas
- * @param frame
- */
- private void drawLaserScanner(Canvas canvas, Rect frame) {
- if(laserStyle != null){
- paint.setColor(laserColor);
- switch (laserStyle){
- case LINE://线
- drawLineScanner(canvas,frame);
- break;
- case GRID://网格
- drawGridScanner(canvas,frame);
- break;
- }
- paint.setShader(null);
- }
- }
-
- /**
- * 绘制线性式扫描
- * @param canvas
- * @param frame
- */
- private void drawLineScanner(Canvas canvas,Rect frame){
- //线性渐变
- LinearGradient linearGradient = new LinearGradient(
- frame.left, scannerStart,
- frame.left, scannerStart + scannerLineHeight,
- shadeColor(laserColor),
- laserColor,
- Shader.TileMode.MIRROR);
-
- paint.setShader(linearGradient);
- if(scannerStart <= scannerEnd) {
- //椭圆
- RectF rectF = new RectF(frame.left + 2 * scannerLineHeight, scannerStart, frame.right - 2 * scannerLineHeight, scannerStart + scannerLineHeight);
- canvas.drawOval(rectF, paint);
- scannerStart += scannerLineMoveDistance;
- } else {
- scannerStart = frame.top;
- }
- }
-
- /**
- * 绘制网格式扫描
- * @param canvas
- * @param frame
- */
- private void drawGridScanner(Canvas canvas,Rect frame){
- int stroke = 2;
- paint.setStrokeWidth(stroke);
- //计算Y轴开始位置
- int startY = gridHeight > 0 && scannerStart - frame.top > gridHeight ? scannerStart - gridHeight : frame.top;
-
- LinearGradient linearGradient = new LinearGradient(frame.left + frame.width()/2, startY, frame.left + frame.width()/2, scannerStart, new int[]{shadeColor(laserColor), laserColor}, new float[]{0,1f}, LinearGradient.TileMode.CLAMP);
- //给画笔设置着色器
- paint.setShader(linearGradient);
-
- float wUnit = frame.width() * 1.0f/ gridColumn;
- float hUnit = wUnit;
- //遍历绘制网格纵线
- for (int i = 1; i < gridColumn; i++) {
- canvas.drawLine(frame.left + i * wUnit, startY,frame.left + i * wUnit, scannerStart,paint);
- }
-
- int height = gridHeight > 0 && scannerStart - frame.top > gridHeight ? gridHeight : scannerStart - frame.top;
-
- //遍历绘制网格横线
- for (int i = 0; i <= height/hUnit; i++) {
- canvas.drawLine(frame.left, scannerStart - i * hUnit,frame.right, scannerStart - i * hUnit,paint);
- }
-
- if(scannerStart width){
+ frameWidth = size;
+ }
+
+ if(frameHeight <= 0 || frameHeight > height){
+ frameHeight = size;
+ }
+
+ if(labelTextWidth <= 0){
+ labelTextWidth = width - getPaddingLeft() - getPaddingRight();
+ }
+
+ float leftOffsets = (width - frameWidth) / 2 + framePaddingLeft - framePaddingRight;
+ float topOffsets = (height - frameHeight) / 2 + framePaddingTop - framePaddingBottom;
+ switch (frameGravity){
+ case LEFT:
+ leftOffsets = framePaddingLeft;
+ break;
+ case TOP:
+ topOffsets = framePaddingTop;
+ break;
+ case RIGHT:
+ leftOffsets = width - frameWidth + framePaddingRight;
+ break;
+ case BOTTOM:
+ topOffsets = height - frameHeight + framePaddingBottom;
+ break;
+ }
+
+ frame = new Rect((int)leftOffsets, (int)topOffsets, (int)leftOffsets + frameWidth, (int)topOffsets + frameHeight);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+
+ if (frame == null) {
+ return;
+ }
+
+ if(scannerStart == 0 || scannerEnd == 0) {
+ scannerStart = frame.top;
+ scannerEnd = frame.bottom - scannerLineHeight;
+ }
+
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+
+ // 绘制模糊区域
+ drawExterior(canvas,frame,width,height);
+ // 绘制扫描动画
+ drawLaserScanner(canvas,frame);
+ // 绘制取景区域框
+ drawFrame(canvas, frame);
+ // 绘制取景区域边角
+ drawCorner(canvas, frame);
+ //绘制提示信息
+ drawTextInfo(canvas, frame);
+ // 间隔更新取景区域
+ postInvalidateDelayed(scannerAnimationDelay, frame.left, frame.top, frame.right, frame.bottom);
+ }
+
+ /**
+ * 绘制文本
+ * @param canvas
+ * @param frame
+ */
+ private void drawTextInfo(Canvas canvas, Rect frame) {
+ if(!TextUtils.isEmpty(labelText)){
+ textPaint.setColor(labelTextColor);
+ textPaint.setTextSize(labelTextSize);
+ textPaint.setTextAlign(Paint.Align.CENTER);
+ StaticLayout staticLayout = new StaticLayout(labelText,textPaint,labelTextWidth, Layout.Alignment.ALIGN_NORMAL,1.2f,0.0f,true);
+ if(labelTextLocation == TextLocation.BOTTOM){
+ canvas.translate(frame.left + frame.width() / 2,frame.bottom + labelTextPadding);
+ }else{
+ canvas.translate(frame.left + frame.width() / 2,frame.top - labelTextPadding - staticLayout.getHeight());
+ }
+ staticLayout.draw(canvas);
+ }
+
+ }
+
+ /**
+ * 绘制边角
+ * @param canvas
+ * @param frame
+ */
+ private void drawCorner(Canvas canvas, Rect frame) {
+ paint.setColor(cornerColor);
+ //左上
+ canvas.drawRect(frame.left, frame.top, frame.left + cornerRectWidth, frame.top + cornerRectHeight, paint);
+ canvas.drawRect(frame.left, frame.top, frame.left + cornerRectHeight, frame.top + cornerRectWidth, paint);
+ //右上
+ canvas.drawRect(frame.right - cornerRectWidth, frame.top, frame.right, frame.top + cornerRectHeight, paint);
+ canvas.drawRect(frame.right - cornerRectHeight, frame.top, frame.right, frame.top + cornerRectWidth, paint);
+ //左下
+ canvas.drawRect(frame.left, frame.bottom - cornerRectWidth, frame.left + cornerRectHeight, frame.bottom, paint);
+ canvas.drawRect(frame.left, frame.bottom - cornerRectHeight, frame.left + cornerRectWidth, frame.bottom, paint);
+ //右下
+ canvas.drawRect(frame.right - cornerRectWidth, frame.bottom - cornerRectHeight, frame.right, frame.bottom, paint);
+ canvas.drawRect(frame.right - cornerRectHeight, frame.bottom - cornerRectWidth, frame.right, frame.bottom, paint);
+ }
+
+ /**
+ * 绘制激光扫描线
+ * @param canvas
+ * @param frame
+ */
+ private void drawLaserScanner(Canvas canvas, Rect frame) {
+ if(laserStyle != null){
+ paint.setColor(laserColor);
+ switch (laserStyle){
+ case LINE://线
+ drawLineScanner(canvas,frame);
+ break;
+ case GRID://网格
+ drawGridScanner(canvas,frame);
+ break;
+ }
+ paint.setShader(null);
+ }
+ }
+
+ /**
+ * 绘制线性式扫描
+ * @param canvas
+ * @param frame
+ */
+ private void drawLineScanner(Canvas canvas,Rect frame){
+ //线性渐变
+ LinearGradient linearGradient = new LinearGradient(
+ frame.left, scannerStart,
+ frame.left, scannerStart + scannerLineHeight,
+ shadeColor(laserColor),
+ laserColor,
+ Shader.TileMode.MIRROR);
+
+ paint.setShader(linearGradient);
+ if(scannerStart <= scannerEnd) {
+ //椭圆
+ RectF rectF = new RectF(frame.left + 2 * scannerLineHeight, scannerStart, frame.right - 2 * scannerLineHeight, scannerStart + scannerLineHeight);
+ canvas.drawOval(rectF, paint);
+ scannerStart += scannerLineMoveDistance;
+ } else {
+ scannerStart = frame.top;
+ }
+ }
+
+ /**
+ * 绘制网格式扫描
+ * @param canvas
+ * @param frame
+ */
+ private void drawGridScanner(Canvas canvas,Rect frame){
+ int stroke = 2;
+ paint.setStrokeWidth(stroke);
+ //计算Y轴开始位置
+ int startY = gridHeight > 0 && scannerStart - frame.top > gridHeight ? scannerStart - gridHeight : frame.top;
+
+ LinearGradient linearGradient = new LinearGradient(frame.left + frame.width()/2, startY, frame.left + frame.width()/2, scannerStart, new int[]{shadeColor(laserColor), laserColor}, new float[]{0,1f}, LinearGradient.TileMode.CLAMP);
+ //给画笔设置着色器
+ paint.setShader(linearGradient);
+
+ float wUnit = frame.width() * 1.0f/ gridColumn;
+ float hUnit = wUnit;
+ //遍历绘制网格纵线
+ for (int i = 1; i < gridColumn; i++) {
+ canvas.drawLine(frame.left + i * wUnit, startY,frame.left + i * wUnit, scannerStart,paint);
+ }
+
+ int height = gridHeight > 0 && scannerStart - frame.top > gridHeight ? gridHeight : scannerStart - frame.top;
+
+ //遍历绘制网格横线
+ for (int i = 0; i <= height/hUnit; i++) {
+ canvas.drawLine(frame.left, scannerStart - i * hUnit,frame.right, scannerStart - i * hUnit,paint);
+ }
+
+ if(scannerStartJenly
- */
-public class AmbientLightManager implements SensorEventListener {
-
- private static final int INTERVAL_TIME = 200;
-
- protected static final float DARK_LUX = 45.0f;
- protected static final float BRIGHT_LUX = 100.0f;
-
- /**
- * 光线太暗时,默认:照度45 lux
- */
- private float darkLightLux = DARK_LUX;
- /**
- * 光线足够亮时,默认:照度100 lux
- */
- private float brightLightLux = BRIGHT_LUX;
-
- private SensorManager sensorManager;
- private Sensor lightSensor;
-
- private long lastTime;
-
- private boolean isLightSensorEnabled;
-
- private OnLightSensorEventListener mOnLightSensorEventListener;
-
- AmbientLightManager(Context context) {
- sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
- isLightSensorEnabled = true;
- }
-
- public void register() {
- if (sensorManager != null && lightSensor != null) {
- sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
- }
- }
-
- public void unregister() {
- if (sensorManager != null && lightSensor != null) {
- sensorManager.unregisterListener(this);
- }
- }
-
-
- @Override
- public void onSensorChanged(SensorEvent sensorEvent) {
- if(isLightSensorEnabled){
- long currentTime = System.currentTimeMillis();
- if(currentTime - lastTime < INTERVAL_TIME){//降低频率
- return;
- }
- lastTime = currentTime;
-
- if (mOnLightSensorEventListener != null) {
- float lightLux = sensorEvent.values[0];
- mOnLightSensorEventListener.onSensorChanged(lightLux);
- if (lightLux <= darkLightLux) {
- mOnLightSensorEventListener.onSensorChanged(true,darkLightLux);
- } else if (lightLux >= brightLightLux) {
- mOnLightSensorEventListener.onSensorChanged(false,darkLightLux);
- }
- }
- }
- }
-
- /**
- * 设置光线足够暗的阈值(单位:lux)
- * @param lightLux
- */
- public void setDarkLightLux(float lightLux){
- this.darkLightLux = lightLux;
- }
-
- /**
- * 设置光线足够明亮的阈值(单位:lux)
- * @param lightLux
- */
- public void setBrightLightLux(float lightLux){
- this.brightLightLux = lightLux;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // do nothing
- }
-
- public boolean isLightSensorEnabled() {
- return isLightSensorEnabled;
- }
-
- /**
- * 设置是否启用光线亮度传感器
- * @param lightSensorEnabled
- */
- public void setLightSensorEnabled(boolean lightSensorEnabled) {
- isLightSensorEnabled = lightSensorEnabled;
- }
-
- /**
- * 设置光线亮度传感器监听器,只有在 {@link #isLightSensorEnabled} 为{@code true} 才有效
- * @param listener
- */
- public void setOnLightSensorEventListener(OnLightSensorEventListener listener){
- mOnLightSensorEventListener = listener;
- }
-
- public interface OnLightSensorEventListener{
- /**
- *
- * @param lightLux 当前检测到的光线照度值
- */
- default void onSensorChanged(float lightLux){
-
- }
-
- /**
- * 传感器改变事件
- * @param dark 是否太暗了,当检测到的光线照度值小于{@link #darkLightLux}时,为{@code true}
- * @param lightLux 当前检测到的光线照度值
- */
- void onSensorChanged(boolean dark,float lightLux);
- }
-}
+package com.king.zxing.manager;
+
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+/**
+ * @author Jenly
+ */
+public class AmbientLightManager implements SensorEventListener {
+
+ private static final int INTERVAL_TIME = 200;
+
+ protected static final float DARK_LUX = 45.0f;
+ protected static final float BRIGHT_LUX = 100.0f;
+
+ /**
+ * 光线太暗时,默认:照度45 lux
+ */
+ private float darkLightLux = DARK_LUX;
+ /**
+ * 光线足够亮时,默认:照度100 lux
+ */
+ private float brightLightLux = BRIGHT_LUX;
+
+ private SensorManager sensorManager;
+ private Sensor lightSensor;
+
+ private long lastTime;
+
+ private boolean isLightSensorEnabled;
+
+ private OnLightSensorEventListener mOnLightSensorEventListener;
+
+ public AmbientLightManager(Context context) {
+ sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ isLightSensorEnabled = true;
+ }
+
+ public void register() {
+ if (sensorManager != null && lightSensor != null) {
+ sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+ public void unregister() {
+ if (sensorManager != null && lightSensor != null) {
+ sensorManager.unregisterListener(this);
+ }
+ }
+
+
+ @Override
+ public void onSensorChanged(SensorEvent sensorEvent) {
+ if(isLightSensorEnabled){
+ long currentTime = System.currentTimeMillis();
+ if(currentTime - lastTime < INTERVAL_TIME){//降低频率
+ return;
+ }
+ lastTime = currentTime;
+
+ if (mOnLightSensorEventListener != null) {
+ float lightLux = sensorEvent.values[0];
+ mOnLightSensorEventListener.onSensorChanged(lightLux);
+ if (lightLux <= darkLightLux) {
+ mOnLightSensorEventListener.onSensorChanged(true,lightLux);
+ } else if (lightLux >= brightLightLux) {
+ mOnLightSensorEventListener.onSensorChanged(false,lightLux);
+ }
+ }
+ }
+ }
+
+ /**
+ * 设置光线足够暗的阈值(单位:lux)
+ * @param lightLux
+ */
+ public void setDarkLightLux(float lightLux){
+ this.darkLightLux = lightLux;
+ }
+
+ /**
+ * 设置光线足够明亮的阈值(单位:lux)
+ * @param lightLux
+ */
+ public void setBrightLightLux(float lightLux){
+ this.brightLightLux = lightLux;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // do nothing
+ }
+
+ public boolean isLightSensorEnabled() {
+ return isLightSensorEnabled;
+ }
+
+ /**
+ * 设置是否启用光线亮度传感器
+ * @param lightSensorEnabled
+ */
+ public void setLightSensorEnabled(boolean lightSensorEnabled) {
+ isLightSensorEnabled = lightSensorEnabled;
+ }
+
+ /**
+ * 设置光线亮度传感器监听器,只有在 {@link #isLightSensorEnabled} 为{@code true} 才有效
+ * @param listener
+ */
+ public void setOnLightSensorEventListener(OnLightSensorEventListener listener){
+ mOnLightSensorEventListener = listener;
+ }
+
+ public interface OnLightSensorEventListener{
+ /**
+ *
+ * @param lightLux 当前检测到的光线照度值
+ */
+ default void onSensorChanged(float lightLux){
+
+ }
+
+ /**
+ * 传感器改变事件
+ * @param dark 是否太暗了,当检测到的光线照度值小于{@link #darkLightLux}时,为{@code true}
+ * @param lightLux 当前检测到的光线照度值
+ */
+ void onSensorChanged(boolean dark,float lightLux);
+ }
+}
diff --git a/lib/src/main/java/com/king/zxing/BeepManager.java b/zxing-lite/src/main/java/com/king/zxing/manager/BeepManager.java
similarity index 94%
rename from lib/src/main/java/com/king/zxing/BeepManager.java
rename to zxing-lite/src/main/java/com/king/zxing/manager/BeepManager.java
index 68aefb6..d9d8bc9 100644
--- a/lib/src/main/java/com/king/zxing/BeepManager.java
+++ b/zxing-lite/src/main/java/com/king/zxing/manager/BeepManager.java
@@ -1,110 +1,111 @@
-package com.king.zxing;
-/*
- * 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.
- */
-
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.os.Vibrator;
-
-import com.king.zxing.util.LogUtils;
-
-import java.io.Closeable;
-
-/**
- * @author Jenly
- */
-public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
-
- private static final long VIBRATE_DURATION = 200L;
-
- private final Context context;
- private MediaPlayer mediaPlayer;
- private Vibrator vibrator;
- private boolean playBeep;
- private boolean vibrate;
-
- BeepManager(Context context) {
- this.context = context;
- this.mediaPlayer = null;
- updatePrefs();
- }
-
- public void setVibrate(boolean vibrate){
- this.vibrate = vibrate;
- }
-
- public void setPlayBeep(boolean playBeep){
- this.playBeep = playBeep;
- }
-
- private synchronized void updatePrefs() {
- if (mediaPlayer == null) {
- mediaPlayer = buildMediaPlayer(context);
- }
- if(vibrator == null){
- vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
- }
- }
-
- public synchronized void playBeepSoundAndVibrate() {
- if (playBeep && mediaPlayer != null) {
- mediaPlayer.start();
- }
- if (vibrate) {
- vibrator.vibrate(VIBRATE_DURATION);
- }
- }
-
- private MediaPlayer buildMediaPlayer(Context context) {
- MediaPlayer mediaPlayer = new MediaPlayer();
- try {
- AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxl_beep);
- mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
- mediaPlayer.setOnErrorListener(this);
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mediaPlayer.setLooping(false);
- mediaPlayer.prepare();
- return mediaPlayer;
- } catch (Exception e) {
- LogUtils.w(e);
- mediaPlayer.release();
- return null;
- }
- }
-
- @Override
- public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
- close();
- updatePrefs();
- return true;
- }
-
- @Override
- public synchronized void close() {
- try{
- if (mediaPlayer != null) {
- mediaPlayer.release();
- mediaPlayer = null;
- }
- }catch (Exception e){
- LogUtils.e(e);
- }
- }
-
+package com.king.zxing.manager;
+/*
+ * 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.
+ */
+
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Vibrator;
+
+import com.king.zxing.R;
+import com.king.zxing.util.LogUtils;
+
+import java.io.Closeable;
+
+/**
+ * @author Jenly
+ */
+public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
+
+ private static final long VIBRATE_DURATION = 200L;
+
+ private final Context context;
+ private MediaPlayer mediaPlayer;
+ private Vibrator vibrator;
+ private boolean playBeep;
+ private boolean vibrate;
+
+ public BeepManager(Context context) {
+ this.context = context;
+ this.mediaPlayer = null;
+ updatePrefs();
+ }
+
+ public void setVibrate(boolean vibrate){
+ this.vibrate = vibrate;
+ }
+
+ public void setPlayBeep(boolean playBeep){
+ this.playBeep = playBeep;
+ }
+
+ private synchronized void updatePrefs() {
+ if (mediaPlayer == null) {
+ mediaPlayer = buildMediaPlayer(context);
+ }
+ if(vibrator == null){
+ vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+ }
+
+ public synchronized void playBeepSoundAndVibrate() {
+ if (playBeep && mediaPlayer != null) {
+ mediaPlayer.start();
+ }
+ if (vibrate) {
+ vibrator.vibrate(VIBRATE_DURATION);
+ }
+ }
+
+ private MediaPlayer buildMediaPlayer(Context context) {
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ try {
+ AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxl_beep);
+ mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
+ mediaPlayer.setOnErrorListener(this);
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mediaPlayer.setLooping(false);
+ mediaPlayer.prepare();
+ return mediaPlayer;
+ } catch (Exception e) {
+ LogUtils.w(e);
+ mediaPlayer.release();
+ return null;
+ }
+ }
+
+ @Override
+ public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
+ close();
+ updatePrefs();
+ return true;
+ }
+
+ @Override
+ public synchronized void close() {
+ try{
+ if (mediaPlayer != null) {
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+ }catch (Exception e){
+ LogUtils.e(e);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/lib/src/main/java/com/king/zxing/util/CodeUtils.java b/zxing-lite/src/main/java/com/king/zxing/util/CodeUtils.java
similarity index 97%
rename from lib/src/main/java/com/king/zxing/util/CodeUtils.java
rename to zxing-lite/src/main/java/com/king/zxing/util/CodeUtils.java
index ce751fe..513b21a 100644
--- a/lib/src/main/java/com/king/zxing/util/CodeUtils.java
+++ b/zxing-lite/src/main/java/com/king/zxing/util/CodeUtils.java
@@ -1,666 +1,666 @@
-/*
- * Copyright (C) 2018 Jenly Yu
- *
- * 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.king.zxing.util;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-
-import android.text.TextPaint;
-import android.text.TextUtils;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.FloatRange;
-import androidx.annotation.NonNull;
-
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.EncodeHintType;
-import com.google.zxing.LuminanceSource;
-import com.google.zxing.MultiFormatReader;
-import com.google.zxing.MultiFormatWriter;
-import com.google.zxing.RGBLuminanceSource;
-import com.google.zxing.Result;
-import com.google.zxing.WriterException;
-import com.google.zxing.common.BitMatrix;
-import com.google.zxing.common.GlobalHistogramBinarizer;
-import com.google.zxing.common.HybridBinarizer;
-import com.google.zxing.qrcode.QRCodeWriter;
-import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
-import com.king.zxing.DecodeFormatManager;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * @author Jenly Jenly
- */
-public final class CodeUtils {
-
- public static final int DEFAULT_REQ_WIDTH = 480;
- public static final int DEFAULT_REQ_HEIGHT = 640;
-
- private CodeUtils(){
- throw new AssertionError();
- }
-
- /**
- * 生成二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix) {
- return createQRCode(content,heightPix,null);
- }
-
- /**
- * 生成二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param codeColor 二维码的颜色
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix,int codeColor) {
- return createQRCode(content,heightPix,null,codeColor);
- }
-
- /**
- * 生成我二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param logo logo大小默认占二维码的20%
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo) {
- return createQRCode(content,heightPix,logo,Color.BLACK);
- }
-
- /**
- * 生成我二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param logo logo大小默认占二维码的20%
- * @param codeColor 二维码的颜色
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,int codeColor) {
- return createQRCode(content,heightPix,logo,0.2f,codeColor);
- }
-
- /**
- * 生成二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param logo 二维码中间的logo
- * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio) {
- //配置参数
- Map hints = new HashMap<>();
- hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
- //容错级别
- hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
- //设置空白边距的宽度
- hints.put(EncodeHintType.MARGIN, 1); //default is 4
- return createQRCode(content,heightPix,logo,ratio,hints);
- }
-
- /**
- * 生成二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param logo 二维码中间的logo
- * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
- * @param codeColor 二维码的颜色
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,int codeColor) {
- //配置参数
- Map hints = new HashMap<>();
- hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
- //容错级别
- hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
- //设置空白边距的宽度
- hints.put(EncodeHintType.MARGIN, 1); //default is 1
- return createQRCode(content,heightPix,logo,ratio,hints,codeColor);
- }
-
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map hints) {
- return createQRCode(content,heightPix,logo,ratio,hints,Color.BLACK);
- }
-
- /**
- * 生成二维码
- * @param content 二维码的内容
- * @param heightPix 二维码的高
- * @param logo 二维码中间的logo
- * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
- * @param hints
- * @param codeColor 二维码的颜色
- * @return
- */
- public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map hints,int codeColor) {
- try {
-
- // 图像数据转换,使用了矩阵转换
- BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints);
- int[] pixels = new int[heightPix * heightPix];
- // 下面这里按照二维码的算法,逐个生成二维码的图片,
- // 两个for循环是图片横列扫描的结果
- for (int y = 0; y < heightPix; y++) {
- for (int x = 0; x < heightPix; x++) {
- if (bitMatrix.get(x, y)) {
- pixels[y * heightPix + x] = codeColor;
- } else {
- pixels[y * heightPix + x] = Color.WHITE;
- }
- }
- }
-
- // 生成二维码图片的格式
- Bitmap bitmap = Bitmap.createBitmap(heightPix, heightPix, Bitmap.Config.ARGB_8888);
- bitmap.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix);
-
- if (logo != null) {
- bitmap = addLogo(bitmap, logo,ratio);
- }
-
- return bitmap;
- } catch (Exception e) {
- LogUtils.w(e.getMessage());
- }
-
- return null;
- }
-
- /**
- * 在二维码中间添加Logo图案
- * @param src
- * @param logo
- * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
- * @return
- */
- private static Bitmap addLogo(Bitmap src, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f) float ratio) {
- 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大小为二维码整体大小
- float scaleFactor = srcWidth * ratio / logoWidth;
- Bitmap bitmap;
- try {
- bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
- 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.save();
- canvas.restore();
- } catch (Exception e) {
- bitmap = null;
- LogUtils.w(e.getMessage());
- }
-
- return bitmap;
- }
-
- /**
- * 解析二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @return
- */
- public static String parseQRCode(String bitmapPath){
- Result result = parseQRCodeResult(bitmapPath);
- if(result != null){
- return result.getText();
- }
- return null;
- }
-
- /**
- * 解析二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @return
- */
- public static Result parseQRCodeResult(String bitmapPath){
- return parseQRCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT);
- }
-
- /**
- * 解析二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
- * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
- * @return
- */
- public static Result parseQRCodeResult(String bitmapPath,int reqWidth,int reqHeight){
- return parseCodeResult(bitmapPath,reqWidth,reqHeight, DecodeFormatManager.QR_CODE_HINTS);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @return
- */
- public static String parseCode(String bitmapPath){
- return parseCode(bitmapPath, DecodeFormatManager.ALL_HINTS);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @param hints 解析编码类型
- * @return
- */
- public static String parseCode(String bitmapPath, Map hints){
- Result result = parseCodeResult(bitmapPath,hints);
- if(result != null){
- return result.getText();
- }
- return null;
- }
-
- /**
- * 解析二维码图片
- * @param bitmap 解析的图片
- * @return
- */
- public static String parseQRCode(Bitmap bitmap){
- return parseCode(bitmap,DecodeFormatManager.QR_CODE_HINTS);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmap 解析的图片
- * @return
- */
- public static String parseCode(Bitmap bitmap){
- return parseCode(bitmap,DecodeFormatManager.ALL_HINTS);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmap 解析的图片
- * @param hints 解析编码类型
- * @return
- */
- public static String parseCode(Bitmap bitmap,Map hints){
- Result result = parseCodeResult(bitmap,hints);
- if(result != null){
- return result.getText();
- }
- return null;
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmapPath
- * @param hints 解析编码类型
- * @return
- */
- public static Result parseCodeResult(String bitmapPath, Map hints){
- return parseCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT,hints);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmapPath 需要解析的图片路径
- * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
- * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
- * @param hints 解析编码类型
- * @return
- */
- public static Result parseCodeResult(String bitmapPath,int reqWidth,int reqHeight, Map hints){
- return parseCodeResult(compressBitmap(bitmapPath,reqWidth,reqHeight),hints);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmap 解析的图片
- * @return
- */
- public static Result parseCodeResult(Bitmap bitmap){
- return parseCodeResult(getRGBLuminanceSource(bitmap),DecodeFormatManager.ALL_HINTS);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param bitmap 解析的图片
- * @param hints 解析编码类型
- * @return
- */
- public static Result parseCodeResult(Bitmap bitmap,Map hints){
- return parseCodeResult(getRGBLuminanceSource(bitmap),hints);
- }
-
- /**
- * 解析一维码/二维码图片
- * @param source
- * @param hints
- * @return
- */
- public static Result parseCodeResult(LuminanceSource source, Map hints){
- Result result = null;
- MultiFormatReader reader = new MultiFormatReader();
- try{
- reader.setHints(hints);
- if (source != null) {
- result = decodeInternal(reader,source);
- if(result == null){
- result = decodeInternal(reader,source.invert());
- }
- if(result == null && source.isRotateSupported()){
- result = decodeInternal(reader,source.rotateCounterClockwise());
- }
- }
-
- }catch (Exception e){
- LogUtils.w(e.getMessage());
- }finally {
- reader.reset();
- }
-
- return result;
- }
-
- private static Result decodeInternal(MultiFormatReader reader,LuminanceSource source){
- Result result = null;
- try{
- try{
- //采用HybridBinarizer解析
- result = reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
- }catch (Exception e){
-
- }
- if(result == null){
- //如果没有解析成功,再采用GlobalHistogramBinarizer解析一次
- result = reader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
- }
- }catch (Exception e){
-
- }
- return result;
- }
-
-
-
- /**
- * 压缩图片
- * @param path
- * @return
- */
- private static Bitmap compressBitmap(String path,int reqWidth,int reqHeight){
- if(reqWidth > 0 && reqHeight > 0){//都大于进行判断是否压缩
-
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
- newOpts.inJustDecodeBounds = true;//获取原始图片大小
- BitmapFactory.decodeFile(path, newOpts);// 此时返回bm为空
- float width = newOpts.outWidth;
- float height = newOpts.outHeight;
- // 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
- int wSize = 1;// wSize=1表示不缩放
- if (width > reqWidth) {// 如果宽度大的话根据宽度固定大小缩放
- wSize = (int) (width / reqWidth);
- }
- int hSize = 1;// wSize=1表示不缩放
- if (height > reqHeight) {// 如果高度高的话根据宽度固定大小缩放
- hSize = (int) (height / reqHeight);
- }
- int size = Math.max(wSize,hSize);
- if (size <= 0)
- size = 1;
- newOpts.inSampleSize = size;// 设置缩放比例
- // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
- newOpts.inJustDecodeBounds = false;
-
- return BitmapFactory.decodeFile(path, newOpts);
-
- }
-
- return BitmapFactory.decodeFile(path);
- }
-
-
- /**
- * 获取RGBLuminanceSource
- * @param bitmap
- * @return
- */
- private static RGBLuminanceSource getRGBLuminanceSource(@NonNull Bitmap bitmap){
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
-
- int[] pixels = new int[width * height];
- bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
- return new RGBLuminanceSource(width, height, pixels);
-
- }
-
- /**
- * 生成条形码
- * @param content
- * @param desiredWidth
- * @param desiredHeight
- * @return
- */
- public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight) {
- return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @return
- */
- public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight) {
- return createBarCode(content,format,desiredWidth,desiredHeight,null);
- }
-
- public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText) {
- return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,Color.BLACK);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param desiredWidth
- * @param desiredHeight
- * @param isShowText
- * @param codeColor
- * @return
- */
- public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) {
- return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,codeColor);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @param hints
- * @return
- */
- public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints) {
- return createBarCode(content,format,desiredWidth,desiredHeight,hints,false,40,Color.BLACK);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @param hints
- * @param isShowText
- * @return
- */
- public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints, boolean isShowText) {
- return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,Color.BLACK);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @param isShowText
- * @param codeColor
- * @return
- */
- public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) {
- return createBarCode(content,format,desiredWidth,desiredHeight,null,isShowText,40,codeColor);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @param hints
- * @param isShowText
- * @return
- */
- public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints, boolean isShowText,@ColorInt int codeColor) {
- return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,codeColor);
- }
-
- /**
- * 生成条形码
- * @param content
- * @param format
- * @param desiredWidth
- * @param desiredHeight
- * @param hints
- * @param isShowText
- * @param textSize
- * @param codeColor
- * @return
- */
- public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight,Map hints,boolean isShowText,int textSize,@ColorInt int codeColor) {
- if(TextUtils.isEmpty(content)){
- return null;
- }
- final int WHITE = Color.WHITE;
- final int BLACK = codeColor;
-
- MultiFormatWriter writer = new MultiFormatWriter();
- try {
- BitMatrix result = writer.encode(content, format, desiredWidth,
- desiredHeight, hints);
- int width = result.getWidth();
- int height = result.getHeight();
- int[] pixels = new int[width * height];
- // All are 0, or black, by default
- for (int y = 0; y < height; y++) {
- int offset = y * width;
- for (int x = 0; x < width; x++) {
- pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
- }
- }
-
- Bitmap bitmap = Bitmap.createBitmap(width, height,
- Bitmap.Config.ARGB_8888);
- bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
- if(isShowText){
- return addCode(bitmap,content,textSize,codeColor,textSize/2);
- }
- return bitmap;
- } catch (WriterException e) {
- LogUtils.w(e.getMessage());
- }
- return null;
- }
-
- /**
- * 条形码下面添加文本信息
- * @param src
- * @param code
- * @param textSize
- * @param textColor
- * @return
- */
- private static Bitmap addCode(Bitmap src, String code, int textSize, @ColorInt int textColor, int offset) {
- if (src == null) {
- return null;
- }
-
- if (TextUtils.isEmpty(code)) {
- return src;
- }
-
- //获取图片的宽高
- int srcWidth = src.getWidth();
- int srcHeight = src.getHeight();
-
- if (srcWidth <= 0 || srcHeight <= 0) {
- return null;
- }
-
- Bitmap bitmap;
- try {
- bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textSize + offset * 2, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- canvas.drawBitmap(src, 0, 0, null);
- TextPaint paint = new TextPaint();
- paint.setTextSize(textSize);
- paint.setColor(textColor);
- paint.setTextAlign(Paint.Align.CENTER);
- canvas.drawText(code,srcWidth/2,srcHeight + textSize /2 + offset,paint);
- canvas.save();
- canvas.restore();
- } catch (Exception e) {
- bitmap = null;
- LogUtils.w(e.getMessage());
- }
-
- return bitmap;
- }
-
-
-
+/*
+ * Copyright (C) 2018 Jenly Yu
+ *
+ * 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.king.zxing.util;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import android.text.TextPaint;
+import android.text.TextUtils;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.RGBLuminanceSource;
+import com.google.zxing.Result;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.GlobalHistogramBinarizer;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.king.zxing.DecodeFormatManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @author Jenly Jenly
+ */
+public final class CodeUtils {
+
+ public static final int DEFAULT_REQ_WIDTH = 480;
+ public static final int DEFAULT_REQ_HEIGHT = 640;
+
+ private CodeUtils(){
+ throw new AssertionError();
+ }
+
+ /**
+ * 生成二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix) {
+ return createQRCode(content,heightPix,null);
+ }
+
+ /**
+ * 生成二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param codeColor 二维码的颜色
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix,int codeColor) {
+ return createQRCode(content,heightPix,null,codeColor);
+ }
+
+ /**
+ * 生成我二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param logo logo大小默认占二维码的20%
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo) {
+ return createQRCode(content,heightPix,logo,Color.BLACK);
+ }
+
+ /**
+ * 生成我二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param logo logo大小默认占二维码的20%
+ * @param codeColor 二维码的颜色
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,int codeColor) {
+ return createQRCode(content,heightPix,logo,0.2f,codeColor);
+ }
+
+ /**
+ * 生成二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param logo 二维码中间的logo
+ * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio) {
+ //配置参数
+ Map hints = new HashMap<>();
+ hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
+ //容错级别
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ //设置空白边距的宽度
+ hints.put(EncodeHintType.MARGIN, 1); //default is 4
+ return createQRCode(content,heightPix,logo,ratio,hints);
+ }
+
+ /**
+ * 生成二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param logo 二维码中间的logo
+ * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
+ * @param codeColor 二维码的颜色
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,int codeColor) {
+ //配置参数
+ Map hints = new HashMap<>();
+ hints.put( EncodeHintType.CHARACTER_SET, "utf-8");
+ //容错级别
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ //设置空白边距的宽度
+ hints.put(EncodeHintType.MARGIN, 1); //default is 1
+ return createQRCode(content,heightPix,logo,ratio,hints,codeColor);
+ }
+
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map hints) {
+ return createQRCode(content,heightPix,logo,ratio,hints,Color.BLACK);
+ }
+
+ /**
+ * 生成二维码
+ * @param content 二维码的内容
+ * @param heightPix 二维码的高
+ * @param logo 二维码中间的logo
+ * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
+ * @param hints
+ * @param codeColor 二维码的颜色
+ * @return
+ */
+ public static Bitmap createQRCode(String content, int heightPix, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f)float ratio,Map hints,int codeColor) {
+ try {
+
+ // 图像数据转换,使用了矩阵转换
+ BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints);
+ int[] pixels = new int[heightPix * heightPix];
+ // 下面这里按照二维码的算法,逐个生成二维码的图片,
+ // 两个for循环是图片横列扫描的结果
+ for (int y = 0; y < heightPix; y++) {
+ for (int x = 0; x < heightPix; x++) {
+ if (bitMatrix.get(x, y)) {
+ pixels[y * heightPix + x] = codeColor;
+ } else {
+ pixels[y * heightPix + x] = Color.WHITE;
+ }
+ }
+ }
+
+ // 生成二维码图片的格式
+ Bitmap bitmap = Bitmap.createBitmap(heightPix, heightPix, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix);
+
+ if (logo != null) {
+ bitmap = addLogo(bitmap, logo,ratio);
+ }
+
+ return bitmap;
+ } catch (Exception e) {
+ LogUtils.w(e.getMessage());
+ }
+
+ return null;
+ }
+
+ /**
+ * 在二维码中间添加Logo图案
+ * @param src
+ * @param logo
+ * @param ratio logo所占比例 因为二维码的最大容错率为30%,所以建议ratio的范围小于0.3
+ * @return
+ */
+ private static Bitmap addLogo(Bitmap src, Bitmap logo,@FloatRange(from = 0.0f,to = 1.0f) float ratio) {
+ 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大小为二维码整体大小
+ float scaleFactor = srcWidth * ratio / logoWidth;
+ Bitmap bitmap;
+ try {
+ bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+ 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.save();
+ canvas.restore();
+ } catch (Exception e) {
+ bitmap = null;
+ LogUtils.w(e.getMessage());
+ }
+
+ return bitmap;
+ }
+
+ /**
+ * 解析二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @return
+ */
+ public static String parseQRCode(String bitmapPath){
+ Result result = parseQRCodeResult(bitmapPath);
+ if(result != null){
+ return result.getText();
+ }
+ return null;
+ }
+
+ /**
+ * 解析二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @return
+ */
+ public static Result parseQRCodeResult(String bitmapPath){
+ return parseQRCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT);
+ }
+
+ /**
+ * 解析二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
+ * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
+ * @return
+ */
+ public static Result parseQRCodeResult(String bitmapPath,int reqWidth,int reqHeight){
+ return parseCodeResult(bitmapPath,reqWidth,reqHeight, DecodeFormatManager.QR_CODE_HINTS);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @return
+ */
+ public static String parseCode(String bitmapPath){
+ return parseCode(bitmapPath, DecodeFormatManager.ALL_HINTS);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @param hints 解析编码类型
+ * @return
+ */
+ public static String parseCode(String bitmapPath, Map hints){
+ Result result = parseCodeResult(bitmapPath,hints);
+ if(result != null){
+ return result.getText();
+ }
+ return null;
+ }
+
+ /**
+ * 解析二维码图片
+ * @param bitmap 解析的图片
+ * @return
+ */
+ public static String parseQRCode(Bitmap bitmap){
+ return parseCode(bitmap,DecodeFormatManager.QR_CODE_HINTS);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmap 解析的图片
+ * @return
+ */
+ public static String parseCode(Bitmap bitmap){
+ return parseCode(bitmap,DecodeFormatManager.ALL_HINTS);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmap 解析的图片
+ * @param hints 解析编码类型
+ * @return
+ */
+ public static String parseCode(Bitmap bitmap,Map hints){
+ Result result = parseCodeResult(bitmap,hints);
+ if(result != null){
+ return result.getText();
+ }
+ return null;
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmapPath
+ * @param hints 解析编码类型
+ * @return
+ */
+ public static Result parseCodeResult(String bitmapPath, Map hints){
+ return parseCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT,hints);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmapPath 需要解析的图片路径
+ * @param reqWidth 请求目标宽度,如果实际图片宽度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
+ * @param reqHeight 请求目标高度,如果实际图片高度大于此值,会自动进行压缩处理,当 reqWidth 和 reqHeight都小于或等于0时,则不进行压缩处理
+ * @param hints 解析编码类型
+ * @return
+ */
+ public static Result parseCodeResult(String bitmapPath,int reqWidth,int reqHeight, Map hints){
+ return parseCodeResult(compressBitmap(bitmapPath,reqWidth,reqHeight),hints);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmap 解析的图片
+ * @return
+ */
+ public static Result parseCodeResult(Bitmap bitmap){
+ return parseCodeResult(getRGBLuminanceSource(bitmap),DecodeFormatManager.ALL_HINTS);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param bitmap 解析的图片
+ * @param hints 解析编码类型
+ * @return
+ */
+ public static Result parseCodeResult(Bitmap bitmap,Map hints){
+ return parseCodeResult(getRGBLuminanceSource(bitmap),hints);
+ }
+
+ /**
+ * 解析一维码/二维码图片
+ * @param source
+ * @param hints
+ * @return
+ */
+ public static Result parseCodeResult(LuminanceSource source, Map hints){
+ Result result = null;
+ MultiFormatReader reader = new MultiFormatReader();
+ try{
+ reader.setHints(hints);
+ if (source != null) {
+ result = decodeInternal(reader,source);
+ if(result == null){
+ result = decodeInternal(reader,source.invert());
+ }
+ if(result == null && source.isRotateSupported()){
+ result = decodeInternal(reader,source.rotateCounterClockwise());
+ }
+ }
+
+ }catch (Exception e){
+ LogUtils.w(e.getMessage());
+ }finally {
+ reader.reset();
+ }
+
+ return result;
+ }
+
+ private static Result decodeInternal(MultiFormatReader reader,LuminanceSource source){
+ Result result = null;
+ try{
+ try{
+ //采用HybridBinarizer解析
+ result = reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
+ }catch (Exception e){
+
+ }
+ if(result == null){
+ //如果没有解析成功,再采用GlobalHistogramBinarizer解析一次
+ result = reader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
+ }
+ }catch (Exception e){
+
+ }
+ return result;
+ }
+
+
+
+ /**
+ * 压缩图片
+ * @param path
+ * @return
+ */
+ private static Bitmap compressBitmap(String path,int reqWidth,int reqHeight){
+ if(reqWidth > 0 && reqHeight > 0){//都大于进行判断是否压缩
+
+ BitmapFactory.Options newOpts = new BitmapFactory.Options();
+ // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
+ newOpts.inJustDecodeBounds = true;//获取原始图片大小
+ BitmapFactory.decodeFile(path, newOpts);// 此时返回bm为空
+ float width = newOpts.outWidth;
+ float height = newOpts.outHeight;
+ // 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
+ int wSize = 1;// wSize=1表示不缩放
+ if (width > reqWidth) {// 如果宽度大的话根据宽度固定大小缩放
+ wSize = (int) (width / reqWidth);
+ }
+ int hSize = 1;// wSize=1表示不缩放
+ if (height > reqHeight) {// 如果高度高的话根据宽度固定大小缩放
+ hSize = (int) (height / reqHeight);
+ }
+ int size = Math.max(wSize,hSize);
+ if (size <= 0)
+ size = 1;
+ newOpts.inSampleSize = size;// 设置缩放比例
+ // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
+ newOpts.inJustDecodeBounds = false;
+
+ return BitmapFactory.decodeFile(path, newOpts);
+
+ }
+
+ return BitmapFactory.decodeFile(path);
+ }
+
+
+ /**
+ * 获取RGBLuminanceSource
+ * @param bitmap
+ * @return
+ */
+ private static RGBLuminanceSource getRGBLuminanceSource(@NonNull Bitmap bitmap){
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ int[] pixels = new int[width * height];
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
+ return new RGBLuminanceSource(width, height, pixels);
+
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param desiredWidth
+ * @param desiredHeight
+ * @return
+ */
+ public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight) {
+ return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @return
+ */
+ public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight) {
+ return createBarCode(content,format,desiredWidth,desiredHeight,null);
+ }
+
+ public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText) {
+ return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,Color.BLACK);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param isShowText
+ * @param codeColor
+ * @return
+ */
+ public static Bitmap createBarCode(String content, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) {
+ return createBarCode(content,BarcodeFormat.CODE_128,desiredWidth,desiredHeight,null,isShowText,40,codeColor);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param hints
+ * @return
+ */
+ public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints) {
+ return createBarCode(content,format,desiredWidth,desiredHeight,hints,false,40,Color.BLACK);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param hints
+ * @param isShowText
+ * @return
+ */
+ public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints, boolean isShowText) {
+ return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,Color.BLACK);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param isShowText
+ * @param codeColor
+ * @return
+ */
+ public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, boolean isShowText,@ColorInt int codeColor) {
+ return createBarCode(content,format,desiredWidth,desiredHeight,null,isShowText,40,codeColor);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param hints
+ * @param isShowText
+ * @return
+ */
+ public static Bitmap createBarCode(String content, BarcodeFormat format, int desiredWidth, int desiredHeight, Map hints, boolean isShowText,@ColorInt int codeColor) {
+ return createBarCode(content,format,desiredWidth,desiredHeight,hints,isShowText,40,codeColor);
+ }
+
+ /**
+ * 生成条形码
+ * @param content
+ * @param format
+ * @param desiredWidth
+ * @param desiredHeight
+ * @param hints
+ * @param isShowText
+ * @param textSize
+ * @param codeColor
+ * @return
+ */
+ public static Bitmap createBarCode(String content,BarcodeFormat format, int desiredWidth, int desiredHeight,Map hints,boolean isShowText,int textSize,@ColorInt int codeColor) {
+ if(TextUtils.isEmpty(content)){
+ return null;
+ }
+ final int WHITE = Color.WHITE;
+ final int BLACK = codeColor;
+
+ MultiFormatWriter writer = new MultiFormatWriter();
+ try {
+ BitMatrix result = writer.encode(content, format, desiredWidth,
+ desiredHeight, hints);
+ int width = result.getWidth();
+ int height = result.getHeight();
+ int[] pixels = new int[width * height];
+ // All are 0, or black, by default
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
+ }
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ if(isShowText){
+ return addCode(bitmap,content,textSize,codeColor,textSize/2);
+ }
+ return bitmap;
+ } catch (WriterException e) {
+ LogUtils.w(e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * 条形码下面添加文本信息
+ * @param src
+ * @param code
+ * @param textSize
+ * @param textColor
+ * @return
+ */
+ private static Bitmap addCode(Bitmap src, String code, int textSize, @ColorInt int textColor, int offset) {
+ if (src == null) {
+ return null;
+ }
+
+ if (TextUtils.isEmpty(code)) {
+ return src;
+ }
+
+ //获取图片的宽高
+ int srcWidth = src.getWidth();
+ int srcHeight = src.getHeight();
+
+ if (srcWidth <= 0 || srcHeight <= 0) {
+ return null;
+ }
+
+ Bitmap bitmap;
+ try {
+ bitmap = Bitmap.createBitmap(srcWidth, srcHeight + textSize + offset * 2, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawBitmap(src, 0, 0, null);
+ TextPaint paint = new TextPaint();
+ paint.setTextSize(textSize);
+ paint.setColor(textColor);
+ paint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText(code,srcWidth/2,srcHeight + textSize /2 + offset,paint);
+ canvas.save();
+ canvas.restore();
+ } catch (Exception e) {
+ bitmap = null;
+ LogUtils.w(e.getMessage());
+ }
+
+ return bitmap;
+ }
+
+
+
}
\ No newline at end of file
diff --git a/lib/src/main/java/com/king/zxing/util/LogUtils.java b/zxing-lite/src/main/java/com/king/zxing/util/LogUtils.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/util/LogUtils.java
rename to zxing-lite/src/main/java/com/king/zxing/util/LogUtils.java
diff --git a/lib/src/main/java/com/king/zxing/util/PermissionUtils.java b/zxing-lite/src/main/java/com/king/zxing/util/PermissionUtils.java
similarity index 100%
rename from lib/src/main/java/com/king/zxing/util/PermissionUtils.java
rename to zxing-lite/src/main/java/com/king/zxing/util/PermissionUtils.java
diff --git a/lib/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png b/zxing-lite/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png
similarity index 100%
rename from lib/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png
rename to zxing-lite/src/main/res/drawable-xxhdpi/zxl_flashlight_off.png
diff --git a/lib/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png b/zxing-lite/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png
similarity index 100%
rename from lib/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png
rename to zxing-lite/src/main/res/drawable-xxhdpi/zxl_flashlight_on.png
diff --git a/lib/src/main/res/drawable/zxl_flashlight_selector.xml b/zxing-lite/src/main/res/drawable/zxl_flashlight_selector.xml
similarity index 100%
rename from lib/src/main/res/drawable/zxl_flashlight_selector.xml
rename to zxing-lite/src/main/res/drawable/zxl_flashlight_selector.xml
diff --git a/lib/src/main/res/layout/zxl_capture.xml b/zxing-lite/src/main/res/layout/zxl_capture.xml
similarity index 100%
rename from lib/src/main/res/layout/zxl_capture.xml
rename to zxing-lite/src/main/res/layout/zxl_capture.xml
diff --git a/lib/src/main/res/raw/zxl_beep.mp3 b/zxing-lite/src/main/res/raw/zxl_beep.mp3
similarity index 100%
rename from lib/src/main/res/raw/zxl_beep.mp3
rename to zxing-lite/src/main/res/raw/zxl_beep.mp3
diff --git a/lib/src/main/res/values/attrs.xml b/zxing-lite/src/main/res/values/attrs.xml
similarity index 97%
rename from lib/src/main/res/values/attrs.xml
rename to zxing-lite/src/main/res/values/attrs.xml
index 51e8d3c..b73f114 100644
--- a/lib/src/main/res/values/attrs.xml
+++ b/zxing-lite/src/main/res/values/attrs.xml
@@ -1,46 +1,46 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/values/colors.xml b/zxing-lite/src/main/res/values/colors.xml
similarity index 97%
rename from lib/src/main/res/values/colors.xml
rename to zxing-lite/src/main/res/values/colors.xml
index ed3984c..62348b6 100644
--- a/lib/src/main/res/values/colors.xml
+++ b/zxing-lite/src/main/res/values/colors.xml
@@ -1,13 +1,13 @@
-
-
-
- #60000000
- #7F1FB3E2
- #FF1FB3E2
- #FF1FB3E2
- #FFC0C0C0
-
- #00000000
- #00000000
-
+
+
+
+ #60000000
+ #7F1FB3E2
+ #FF1FB3E2
+ #FF1FB3E2
+ #FFC0C0C0
+
+ #00000000
+ #00000000
+
\ No newline at end of file
diff --git a/lib/src/main/res/values/dimens.xml b/zxing-lite/src/main/res/values/dimens.xml
similarity index 100%
rename from lib/src/main/res/values/dimens.xml
rename to zxing-lite/src/main/res/values/dimens.xml
diff --git a/lib/src/main/res/values/styles.xml b/zxing-lite/src/main/res/values/styles.xml
similarity index 100%
rename from lib/src/main/res/values/styles.xml
rename to zxing-lite/src/main/res/values/styles.xml
diff --git a/lib/src/test/java/com/king/zxing/ExampleUnitTest.java b/zxing-lite/src/test/java/com/king/zxing/ExampleUnitTest.java
similarity index 95%
rename from lib/src/test/java/com/king/zxing/ExampleUnitTest.java
rename to zxing-lite/src/test/java/com/king/zxing/ExampleUnitTest.java
index 9d0a385..efef26a 100644
--- a/lib/src/test/java/com/king/zxing/ExampleUnitTest.java
+++ b/zxing-lite/src/test/java/com/king/zxing/ExampleUnitTest.java
@@ -1,17 +1,17 @@
-package com.king.zxing;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
+package com.king.zxing;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
}
\ No newline at end of file