2023年06月06日18:19:36
自定义二维码扫码页
| | |
| | | <component name="DesignSurface"> |
| | | <option name="filePathToZoomLevelMap"> |
| | | <map> |
| | | <entry key="../../../../../layout/custom_preview.xml" value="0.22923076923076924" /> |
| | | <entry key="../../../.gradle/caches/transforms-2/files-2.1/7c2cbaa925629e10707989293f119941/jetified-zxing-android-embedded-3.4.0/res/layout/zxing_barcode_scanner.xml" value="0.20625" /> |
| | | <entry key="../../../.gradle/caches/transforms-2/files-2.1/7c2cbaa925629e10707989293f119941/jetified-zxing-android-embedded-3.4.0/res/layout/zxing_capture.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_1.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_123.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_capture.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_fast_scan.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_home_login.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_my_power_station.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/activity_new_capture.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/fragment_house_list.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/fragment_me.xml" value="0.1" /> |
| | | <entry key="app/src/main/res/layout/frgment_house_list_line.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/toolbar_top_view_52.xml" value="0.20625" /> |
| | | <entry key="app/src/main/res/layout/toolbar_top_view_53.xml" value="0.20625" /> |
| | | <entry key="third-zxing/src/main/res/drawable/transparent_divider.xml" value="0.1275" /> |
| | | <entry key="third-zxing/src/main/res/layout/activity_capture.xml" value="0.20625" /> |
| | | <entry key="third-zxing/src/main/res/layout/toolbar.xml" value="0.20625" /> |
| | | <entry key="third-zxing/src/main/res/layout/toolbar_top_view.xml" value="0.20625" /> |
| | | <entry key="third-zxing/src/main/res/layout/toolbar_top_view_52.xml" value="0.20625" /> |
| | | </map> |
| | | </option> |
| | | </component> |
| | |
| | | implementation 'androidx.navigation:navigation-fragment:2.3.5' |
| | | implementation 'androidx.navigation:navigation-ui:2.3.5' |
| | | implementation 'androidx.legacy:legacy-support-v4:1.0.0' |
| | | implementation project(path: ':third-zxing') |
| | | testImplementation 'junit:junit:4.13.2' |
| | | androidTestImplementation 'androidx.test.ext:junit:1.1.3' |
| | | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' |
| | | implementation 'org.greenrobot:eventbus:3.0.0' |
| | | |
| | | |
| | | } |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.hdl.photovoltaic"> |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | package="com.hdl.photovoltaic" > |
| | | |
| | | <uses-permission |
| | | android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" |
| | | tools:ignore="ProtectedPermissions" /> |
| | | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
| | | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
| | | <uses-permission android:name="android.permission.CAMERA" /> |
| | | <uses-permission android:name="android.permission.VIBRATE" /> |
| | | |
| | | <application |
| | | android:name=".HDLApp" |
| | |
| | | android:label="@string/app_name" |
| | | android:roundIcon="@mipmap/ic_launcher_round" |
| | | android:supportsRtl="true" |
| | | android:theme="@style/Theme.PhotovoltaicDebug"> |
| | | android:theme="@style/Theme.PhotovoltaicDebug" > |
| | | <activity |
| | | android:name=".ui.device.FastScanActivity" |
| | | android:exported="false" /> |
| | |
| | | android:exported="false" /> |
| | | <activity |
| | | android:name=".ui.StartActivity" |
| | | android:exported="true"> |
| | | android:exported="true" > |
| | | <intent-filter> |
| | | <action android:name="android.intent.action.MAIN" /> |
| | | |
| | |
| | | /** |
| | | * 显示View |
| | | * |
| | | * @param view |
| | | * @param view - |
| | | */ |
| | | public void setViewVisible(View view) { |
| | | if (view.getVisibility() != View.VISIBLE && _mActivity != null) { |
| | |
| | | /** |
| | | * 隐藏View |
| | | * |
| | | * @param view |
| | | * @param view - |
| | | */ |
| | | protected void setViewGone(View view) { |
| | | if (view.getVisibility() != View.GONE && _mActivity != null) { |
| | |
| | | /** |
| | | * 简单的跳转Activity |
| | | * |
| | | * @param clazz |
| | | * @param clazz - |
| | | */ |
| | | protected void startActivity(Class<?> clazz) { |
| | | if (_mActivity != null) { |
| | |
| | | |
| | | private void bottomViewChangeOfStyle() { |
| | | if (this.currentFragmentIndex == 0) { |
| | | |
| | | viewBinding.myPowerStationFcv1.setVisibility(View.VISIBLE); |
| | | viewBinding.myPowerStationFcv2.setVisibility(View.GONE); |
| | | viewBinding.myPowerStationBottomIl1.iconIv.setImageDrawable(AppCompatResources.getDrawable(_mActivity, R.drawable.selectedpowerstation)); |
| | |
| | | package com.hdl.photovoltaic.ui.device; |
| | | |
| | | import androidx.appcompat.app.AppCompatActivity; |
| | | import androidx.appcompat.content.res.AppCompatResources; |
| | | |
| | | import android.content.Intent; |
| | | import android.database.Cursor; |
| | | import android.graphics.Rect; |
| | | import android.hardware.Camera; |
| | | import android.net.Uri; |
| | | import android.os.Bundle; |
| | | import android.os.Handler; |
| | | import android.provider.MediaStore; |
| | | import android.text.TextUtils; |
| | | import android.util.Log; |
| | | import android.view.SurfaceHolder; |
| | | import android.view.SurfaceView; |
| | | import android.view.View; |
| | | import android.view.animation.Animation; |
| | | import android.view.animation.TranslateAnimation; |
| | | import android.widget.ImageView; |
| | | import android.widget.RelativeLayout; |
| | | import android.widget.Toast; |
| | | |
| | | import com.google.zxing.BinaryBitmap; |
| | | import com.google.zxing.ChecksumException; |
| | | import com.google.zxing.DecodeHintType; |
| | | import com.google.zxing.FormatException; |
| | | import com.google.zxing.NotFoundException; |
| | | import com.google.zxing.Result; |
| | | import com.google.zxing.common.HybridBinarizer; |
| | | import com.google.zxing.qrcode.QRCodeReader; |
| | | import com.hdl.photovoltaic.R; |
| | | import com.hdl.photovoltaic.base.CustomBaseActivity; |
| | | import com.hdl.photovoltaic.databinding.ActivityFastScanBinding; |
| | | import com.zxing.IZxingActivity; |
| | | import com.zxing.camera.CameraManager; |
| | | import com.zxing.decode.DecodeThread; |
| | | import com.zxing.decode.RGBLuminanceSource; |
| | | import com.zxing.utils.BeepManager; |
| | | import com.zxing.utils.CaptureActivityHandler; |
| | | import com.zxing.utils.InactivityTimer; |
| | | |
| | | public class FastScanActivity extends CustomBaseActivity { |
| | | import java.io.FileNotFoundException; |
| | | import java.io.IOException; |
| | | import java.lang.reflect.Field; |
| | | import java.util.Hashtable; |
| | | |
| | | |
| | | public class FastScanActivity extends CustomBaseActivity implements IZxingActivity, SurfaceHolder.Callback { |
| | | private static final String TAG = com.zxing.qrcode.CaptureActivity.class.getSimpleName(); |
| | | private final int REQUEST_CODE = 33; |
| | | private CameraManager cameraManager; |
| | | private CaptureActivityHandler handler; |
| | | private InactivityTimer inactivityTimer; |
| | | private BeepManager beepManager; |
| | | private SurfaceView scanPreview = null; |
| | | private RelativeLayout scanContainer; |
| | | private RelativeLayout scanCropView; |
| | | private Rect mCropRect = null; |
| | | private boolean isHasSurface = false; |
| | | private ActivityFastScanBinding viewBinding; |
| | | |
| | | @Override |
| | | public Handler getHandler() { |
| | | return handler; |
| | | } |
| | | |
| | | @Override |
| | | public void drawViewfinder() { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public CameraManager getCameraManager() { |
| | | return cameraManager; |
| | | } |
| | | |
| | | @Override |
| | | public Object getContentView() { |
| | |
| | | |
| | | @Override |
| | | public void onBindView(Bundle savedInstanceState) { |
| | | // Window window = getWindow(); |
| | | // window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
| | | // //设置根视图 |
| | | // View mContentView = LayoutInflater.from(this).inflate(R.layout.activity_new_capture, null); |
| | | // setContentView(mContentView); |
| | | //初始化 |
| | | initView(savedInstanceState); |
| | | //初始化界面监听器 |
| | | initEvent(); |
| | | } |
| | | |
| | | |
| | | private void initView(Bundle savedInstanceState) { |
| | | |
| | | |
| | | scanPreview = findViewById(R.id.new_capture_preview); |
| | | scanContainer = findViewById(R.id.new_capture_rl); |
| | | scanCropView = findViewById(R.id.new_capture_crop_view); |
| | | ImageView scanLine = findViewById(R.id.new_capture_scan_line); |
| | | inactivityTimer = new InactivityTimer(this); |
| | | beepManager = new BeepManager(this); |
| | | |
| | | TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation |
| | | .RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, |
| | | 0.9f); |
| | | animation.setDuration(2000); |
| | | animation.setRepeatCount(-1); |
| | | animation.setRepeatMode(Animation.RESTART); |
| | | scanLine.startAnimation(animation); |
| | | } |
| | | |
| | | private void initEvent() { |
| | | viewBinding.newTopBackBtn.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | finish(); |
| | | } |
| | | }); |
| | | |
| | | viewBinding.newTopMoreBtn.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | //跳到入网 |
| | | } |
| | | }); |
| | | |
| | | viewBinding.newLightIv.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | v.setSelected(!v.isSelected()); |
| | | if (v.isSelected()) { |
| | | viewBinding.newLightIv.setImageDrawable(AppCompatResources.getDrawable(FastScanActivity.this, com.zxing.R.drawable.fast_scan_light_open)); |
| | | } else { |
| | | viewBinding.newLightIv.setImageDrawable(AppCompatResources.getDrawable(FastScanActivity.this, com.zxing.R.drawable.fast_scan_light_close)); |
| | | } |
| | | flashLightOnOrOff(); |
| | | |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | | public void enterGallery() { |
| | | // 进入图库 |
| | | Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
| | | intent.addCategory(Intent.CATEGORY_OPENABLE); |
| | | intent.setType("image/*"); |
| | | intent.putExtra("return-data", true); |
| | | startActivityForResult(intent, REQUEST_CODE); |
| | | } |
| | | |
| | | @Override |
| | | protected void onResume() { |
| | | super.onResume(); |
| | | cameraManager = new CameraManager(getApplication()); |
| | | handler = null; |
| | | if (isHasSurface) { |
| | | initCamera(scanPreview.getHolder()); |
| | | } else { |
| | | scanPreview.getHolder().addCallback(this); |
| | | } |
| | | |
| | | inactivityTimer.onResume(); |
| | | } |
| | | |
| | | @Override |
| | | protected void onPause() { |
| | | if (handler != null) { |
| | | handler.quitSynchronously(); |
| | | handler = null; |
| | | } |
| | | inactivityTimer.onPause(); |
| | | beepManager.close(); |
| | | cameraManager.closeDriver(); |
| | | if (!isHasSurface) { |
| | | scanPreview.getHolder().removeCallback(this); |
| | | } |
| | | super.onPause(); |
| | | } |
| | | |
| | | @Override |
| | | protected void onDestroy() { |
| | | inactivityTimer.shutdown(); |
| | | super.onDestroy(); |
| | | } |
| | | |
| | | @Override |
| | | public void surfaceCreated(SurfaceHolder holder) { |
| | | if (holder == null) { |
| | | Log.d(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!"); |
| | | } |
| | | if (!isHasSurface) { |
| | | isHasSurface = true; |
| | | initCamera(holder); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void surfaceDestroyed(SurfaceHolder holder) { |
| | | isHasSurface = false; |
| | | } |
| | | |
| | | @Override |
| | | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| | | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A valid barcode has been found, so give an indication of success and show |
| | | * the results. |
| | | * |
| | | * @param rawResult The contents of the barcode. |
| | | * @param bundle The extras |
| | | */ |
| | | @Override |
| | | public void handleDecode(Result rawResult, Bundle bundle) { |
| | | inactivityTimer.onActivity(); |
| | | beepManager.playBeepSoundAndVibrate(); |
| | | doProcess(rawResult.getText()); |
| | | restartPreviewAfterDelay(3000); |
| | | } |
| | | |
| | | private void initCamera(SurfaceHolder surfaceHolder) { |
| | | if (surfaceHolder == null) { |
| | | throw new IllegalStateException("No SurfaceHolder provided"); |
| | | } |
| | | if (cameraManager.isOpen()) { |
| | | Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?"); |
| | | return; |
| | | } |
| | | try { |
| | | cameraManager.openDriver(surfaceHolder); |
| | | // Creating the handler starts the preview, which can also throw a |
| | | // RuntimeException. |
| | | if (handler == null) { |
| | | handler = new CaptureActivityHandler(this, cameraManager, DecodeThread.ALL_MODE); |
| | | } |
| | | |
| | | initCrop(); |
| | | } catch (IOException ioe) { |
| | | Log.w(TAG, ioe); |
| | | Toast.makeText(this, com.zxing.R.string.capture_no_camera, Toast.LENGTH_SHORT).show(); |
| | | finish(); |
| | | } catch (RuntimeException e) { |
| | | Log.w(TAG, "Unexpected error initializing camera", e); |
| | | Toast.makeText(this, com.zxing.R.string.capture_no_camera, Toast.LENGTH_SHORT).show(); |
| | | finish(); |
| | | } |
| | | } |
| | | |
| | | public void restartPreviewAfterDelay(long delayMS) { |
| | | if (handler != null) { |
| | | handler.sendEmptyMessageDelayed(com.zxing.R.id.restart_preview, delayMS); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Rect getCropRect() { |
| | | return mCropRect; |
| | | } |
| | | |
| | | /** |
| | | * 初始化截取的矩形区域 |
| | | */ |
| | | private void initCrop() { |
| | | int cameraWidth = cameraManager.getCameraResolution().y; |
| | | int cameraHeight = cameraManager.getCameraResolution().x; |
| | | |
| | | /** 获取布局中扫描框的位置信息 */ |
| | | int[] location = new int[2]; |
| | | scanCropView.getLocationInWindow(location); |
| | | |
| | | int cropLeft = location[0]; |
| | | int cropTop = location[1] - getStatusBarHeight(); |
| | | |
| | | int cropWidth = scanCropView.getWidth(); |
| | | int cropHeight = scanCropView.getHeight(); |
| | | |
| | | /** 获取布局容器的宽高 */ |
| | | int containerWidth = scanContainer.getWidth(); |
| | | int containerHeight = scanContainer.getHeight(); |
| | | |
| | | /** 计算最终截取的矩形的左上角顶点x坐标 */ |
| | | int x = cropLeft * cameraWidth / containerWidth; |
| | | /** 计算最终截取的矩形的左上角顶点y坐标 */ |
| | | int y = cropTop * cameraHeight / containerHeight; |
| | | |
| | | /** 计算最终截取的矩形的宽度 */ |
| | | int width = cropWidth * cameraWidth / containerWidth; |
| | | /** 计算最终截取的矩形的高度 */ |
| | | int height = cropHeight * cameraHeight / containerHeight; |
| | | |
| | | /** 生成最终的截取的矩形 */ |
| | | mCropRect = new Rect(x, y, width + x, height + y); |
| | | } |
| | | |
| | | private int getStatusBarHeight() { |
| | | try { |
| | | Class<?> c = Class.forName("com.android.internal.R$dimen"); |
| | | Object obj = c.newInstance(); |
| | | Field field = c.getField("status_bar_height"); |
| | | int x = Integer.parseInt(field.get(obj).toString()); |
| | | return getResources().getDimensionPixelSize(x); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | private void doProcess(String result) { |
| | | Log.d("panlili", "scanResult: " + result); |
| | | /*if (!DeviceHelper.getNetworkState()) { |
| | | Toast.makeText(this, R.string.capture_no_network, Toast.LENGTH_SHORT).show(); |
| | | return; |
| | | }*/ |
| | | |
| | | if (TextUtils.isEmpty(result)) { |
| | | Toast.makeText(this, com.zxing.R.string.capture_no_result, Toast.LENGTH_SHORT).show(); |
| | | } else { |
| | | Intent intent = new Intent(); |
| | | intent.putExtra("data", result); |
| | | setResult(RESULT_OK, intent); |
| | | finish(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| | | super.onActivityResult(requestCode, resultCode, data); |
| | | if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) { |
| | | Uri originalUri = data.getData(); |
| | | if (originalUri != null) { |
| | | String path = originalUri.getPath(); |
| | | String[] proj = {MediaStore.Images.Media.DATA}; |
| | | Cursor cursor = getContentResolver().query(originalUri, proj, null, null, null); |
| | | if (cursor != null) { |
| | | int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); |
| | | cursor.moveToFirst(); |
| | | path = cursor.getString(column_index); |
| | | cursor.close(); |
| | | } |
| | | |
| | | if (!TextUtils.isEmpty(path)) { |
| | | handleQRCodeFormPhoto(path); |
| | | } else { |
| | | Toast.makeText(this, "图片已损坏,请重新选择!", Toast.LENGTH_SHORT).show(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 解析图库选择的二维码 |
| | | */ |
| | | public void handleQRCodeFormPhoto(final String filePath) { |
| | | Thread dealThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | Hashtable<DecodeHintType, String> hints = new Hashtable<>(); |
| | | hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); |
| | | RGBLuminanceSource source = null; |
| | | try { |
| | | source = new RGBLuminanceSource(filePath); |
| | | } catch (FileNotFoundException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source)); |
| | | QRCodeReader reader = new QRCodeReader(); |
| | | Result result; |
| | | try { |
| | | result = reader.decode(binaryBitmap, hints); |
| | | if (!TextUtils.isEmpty(result.getText())) { |
| | | dealUIInfo(result.getText()); |
| | | } else { |
| | | dealUIInfo(null); |
| | | } |
| | | } catch (NotFoundException | ChecksumException | FormatException e) { |
| | | dealUIInfo(null); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | }); |
| | | dealThread.start(); |
| | | } |
| | | |
| | | private void dealUIInfo(final Object progressInfo) { |
| | | runOnUiThread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | if (progressInfo == null) { |
| | | Toast.makeText(FastScanActivity.this, com.zxing.R.string.capture_no_result2, Toast.LENGTH_SHORT).show(); |
| | | } else { |
| | | doProcess(progressInfo.toString()); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 闪光灯开启或者关闭 |
| | | */ |
| | | private void flashLightOnOrOff() { |
| | | // |
| | | android.hardware.Camera camera = cameraManager.getCamera(); |
| | | if (camera == null) { |
| | | return; |
| | | } |
| | | android.hardware.Camera.Parameters parameters = camera.getParameters(); |
| | | // 判断闪光灯当前状态 |
| | | if (Camera.Parameters.FLASH_MODE_OFF.equals(parameters.getFlashMode())) { |
| | | onLight(camera, parameters); |
| | | } else if (Camera.Parameters.FLASH_MODE_TORCH.equals(parameters.getFlashMode())) { |
| | | offLight(camera, parameters); |
| | | } |
| | | } |
| | | |
| | | private void onLight(android.hardware.Camera camera, Camera.Parameters parameters) { |
| | | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); |
| | | camera.setParameters(parameters); |
| | | } |
| | | |
| | | private void offLight(android.hardware.Camera camera, Camera.Parameters parameters) { |
| | | parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); |
| | | camera.setParameters(parameters); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.hdl.photovoltaic.ui.powerstation; |
| | | |
| | | import android.content.Context; |
| | | import android.content.Intent; |
| | | import android.hardware.camera2.CameraManager; |
| | | import android.os.Bundle; |
| | | import android.util.Log; |
| | |
| | | import com.hdl.photovoltaic.base.CustomBaseFragment; |
| | | import com.hdl.photovoltaic.ui.adapter.HouseInfoAdapter; |
| | | import com.hdl.photovoltaic.ui.bean.HouseInfoBean; |
| | | import com.hdl.photovoltaic.ui.device.FastScanActivity; |
| | | import com.hdl.photovoltaic.utils.FlashLightUtils; |
| | | |
| | | import java.util.ArrayList; |
| | |
| | | public class HouseListFragment extends CustomBaseFragment { |
| | | private FragmentHouseListBinding viewBinding; |
| | | private HouseInfoAdapter houseInfoAdapter; |
| | | private FlashLightUtils flashLightUtils; |
| | | private CameraManager manager; |
| | | |
| | | private List<HouseInfoBean> houseInfoBeanList = null; |
| | |
| | | |
| | | @Override |
| | | public void onBindView(Bundle savedInstanceState) { |
| | | flashLightUtils = new FlashLightUtils(_mActivity); |
| | | manager = (CameraManager) _mActivity.getSystemService(Context.CAMERA_SERVICE); |
| | | |
| | | initData(); |
| | |
| | | |
| | | |
| | | private void initEvent() { |
| | | |
| | | |
| | | viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | |
| | | Intent intent = new Intent(); |
| | | intent.setClass(_mActivity, FastScanActivity.class); |
| | | startActivity(intent); |
| | | } |
| | | }); |
| | | //设置下拉箭头颜色 |
| | |
| | | viewBinding.toolbarTopFragmentHouseListRl.topTitleTv.setText(R.string.my_power_station_我的电站); |
| | | viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setVisibility(View.VISIBLE); |
| | | viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setImageResource(R.drawable.add); |
| | | |
| | | LinearLayoutManager linearLayout = new LinearLayoutManager(_mActivity); |
| | | houseInfoAdapter = new HouseInfoAdapter(this.houseInfoBeanList); |
| | | viewBinding.fragmentHouseSrlListRc.setLayoutManager(linearLayout); |
| | |
| | | houseInfoBean.setName("电站" + i); |
| | | this.houseInfoBeanList.add(houseInfoBean); |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | private void openCamera() { |
| | | |
| | | |
| | | } |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:id="@+id/new_capture_rl" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | tools:context=".ui.device.FastScanActivity"> |
| | | android:background="#636363"> |
| | | |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
| | | <RelativeLayout |
| | | android:id="@+id/new_top_bar_view" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="52dp" |
| | | android:background="#245EC3" |
| | | android:orientation="horizontal"> |
| | | |
| | | <!--1.返回按钮 增大点击区域--> |
| | | <LinearLayout |
| | | android:id="@+id/new_top_back_btn" |
| | | android:layout_width="56dp" |
| | | android:layout_height="match_parent" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <ImageView |
| | | android:layout_width="24dp" |
| | | android:layout_height="24dp" |
| | | android:layout_gravity="center" |
| | | android:layout_marginLeft="16dp" |
| | | android:scaleType="centerInside" |
| | | android:src="@drawable/back" /> |
| | | </LinearLayout> |
| | | |
| | | <!--2.标题文本--> |
| | | <TextView |
| | | android:id="@+id/new_top_title_tv" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_centerInParent="true" |
| | | android:layout_marginLeft="60dp" |
| | | android:layout_marginRight="60dp" |
| | | android:fontFamily="sans-serif-medium" |
| | | android:gravity="center" |
| | | android:maxLines="1" |
| | | android:text="快速扫码" |
| | | android:textColor="#FFFFFFFF" |
| | | android:textSize="18sp" /> |
| | | |
| | | <!--3.更多按钮 默认隐藏--> |
| | | <LinearLayout |
| | | android:id="@+id/new_top_more_btn" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="match_parent" |
| | | android:layout_alignParentEnd="true" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:id="@+id/new_top_connect_tv" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="20dp" |
| | | android:layout_marginEnd="20dp" |
| | | android:text="@string/device_手动连接" |
| | | android:textColor="@color/text_FFFFFFFF" |
| | | android:textSize="@dimen/text_14" /> |
| | | |
| | | </LinearLayout> |
| | | |
| | | </RelativeLayout> |
| | | |
| | | <SurfaceView |
| | | android:id="@+id/new_capture_preview" |
| | | android:layout_width="367dp" |
| | | android:layout_height="367dp" |
| | | android:layout_centerInParent="true" /> |
| | | |
| | | <RelativeLayout |
| | | android:id="@+id/new_capture_crop_view" |
| | | android:layout_width="367dp" |
| | | android:layout_height="367dp" |
| | | android:layout_centerInParent="true" |
| | | android:background="@drawable/scan_capture"> |
| | | |
| | | <RelativeLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="367dp" |
| | | android:layout_marginTop="182dp"> |
| | | |
| | | <ImageView |
| | | android:id="@+id/new_capture_scan_line" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_alignParentTop="true" |
| | | android:layout_marginTop="5dp" |
| | | android:layout_marginBottom="5dp" |
| | | android:src="@drawable/scan_line" /> |
| | | </RelativeLayout> |
| | | |
| | | </RelativeLayout> |
| | | |
| | | <ImageView |
| | | android:id="@+id/new_light_iv" |
| | | android:layout_width="127dp" |
| | | android:layout_height="127dp" |
| | | android:layout_alignParentBottom="true" |
| | | android:layout_centerInParent="true" |
| | | android:layout_marginBottom="13dp" |
| | | android:background="@drawable/fast_scan_light_close" /> |
| | | |
| | | </RelativeLayout> |
| | |
| | | android:scaleType="centerInside" |
| | | android:visibility="gone" /> |
| | | |
| | | |
| | | </LinearLayout> |
| | | |
| | | </RelativeLayout> |
| | |
| | | <color name="text_06B92A">#06B92A</color> |
| | | <color name="text_F9FAFB">#F9FAFB</color> |
| | | <color name="text_CBCDD1">#CBCDD1</color>/ |
| | | <color name="text_FF245EC3">#FF245EC3</color> |
| | | <color name="text_FF245EC3">#FF245EC3</color># |
| | | <color name="text_636363">#636363</color> |
| | | |
| | | </resources> |
| | |
| | | <string name="my_power_station_离线">离线</string> |
| | | <string name="my_power_station_故障">故障</string> |
| | | <string name="my_power_station_运行">运行</string> |
| | | <string name="device_手动连接">手动连接</string> |
| | | |
| | | |
| | | <!--我的--> |
| | |
| | | # Enables namespacing of each library's R class so that its R class includes only the |
| | | # resources declared in the library itself and none from the library's dependencies, |
| | | # thereby reducing the size of the R class for that library |
| | | android.nonTransitiveRClass=true |
| | | android.nonTransitiveRClass=true |
| | | # 可以将v4,v7库转成Android X |
| | | android.enableJetifier=true |
| | |
| | | rootProject.name = "PhotovoltaicDebug" |
| | | include ':app' |
| | | include ':third-zxing' |
New file |
| | |
| | | apply plugin: 'com.android.library'
|
| | |
|
| | |
|
| | | android {
|
| | | compileSdkVersion 29
|
| | | buildToolsVersion "29.0.1"
|
| | |
|
| | | defaultConfig {
|
| | | minSdkVersion 21
|
| | | targetSdkVersion 26
|
| | | versionCode 1
|
| | | versionName "1.0"
|
| | |
|
| | | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
| | | javaCompileOptions {
|
| | | annotationProcessorOptions {
|
| | | arguments = [moduleName: project.getName()]
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | | buildTypes {
|
| | | release {
|
| | | minifyEnabled false
|
| | | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
| | | }
|
| | | }
|
| | | android {
|
| | | lintOptions {
|
| | | abortOnError false
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | dependencies {
|
| | | api fileTree(include: ['*.jar'], dir: 'libs')
|
| | | // compileOnly fileTree(include: ['*.jar'], dir: 'libs')
|
| | | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
| | | exclude group: 'com.android.support', module: 'support-annotations'
|
| | | })
|
| | | testImplementation 'junit:junit:4.12'
|
| | | implementation 'com.android.support:support-annotations:28.0.0'
|
| | | // api 'com.android.support:appcompat-v7:28.0.0'
|
| | | implementation 'androidx.appcompat:appcompat:1.3.0'
|
| | | }
|
New file |
| | |
| | | # Add project specific ProGuard rules here.
|
| | | # By default, the flags in this file are appended to flags specified
|
| | | # in /Users/ye/Library/Android/sdk/tools/proguard/proguard-android.txt
|
| | | # You can edit the include path and order by changing the proguardFiles
|
| | | # directive in build.gradle.
|
| | | #
|
| | | # For more details, see
|
| | | # http://developer.android.com/guide/developing/tools/proguard.html
|
| | |
|
| | | # Add any project specific keep options here:
|
| | |
|
| | | # 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
|
New file |
| | |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
| | | package="com.zxing">
|
| | |
|
| | | <application >
|
| | |
|
| | | <activity
|
| | | android:name=".qrcode.CaptureActivity"
|
| | | android:configChanges="orientation|keyboardHidden|screenSize|locale"
|
| | | android:exported="false"
|
| | | android:screenOrientation="portrait"
|
| | | android:theme="@style/AppTheme.Base"/>
|
| | | </application>
|
| | |
|
| | | </manifest>
|
New file |
| | |
| | | package com.zxing;
|
| | |
|
| | | import android.app.Activity;
|
| | | import android.app.Application;
|
| | | import android.content.Context;
|
| | | import android.content.Intent;
|
| | | import android.content.pm.PackageInfo;
|
| | | import android.content.pm.PackageManager;
|
| | | import android.content.res.Resources;
|
| | | import android.os.Build;
|
| | |
|
| | | import com.zxing.utils.Strings;
|
| | |
|
| | | public final class ContextHelper {
|
| | |
|
| | | private static Application application;
|
| | | private static Class splashCls;
|
| | |
|
| | | /**
|
| | | * 初始化
|
| | | *
|
| | | * @param application app
|
| | | */
|
| | | public static void init(Application application) {
|
| | | if (ContextHelper.application == null) {
|
| | | ContextHelper.application = application;
|
| | | }
|
| | | }
|
| | |
|
| | | public static Context getAppContext() {
|
| | | if (application != null) {
|
| | | return application.getApplicationContext();
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | | public static Application getApp() {
|
| | | return application;
|
| | | }
|
| | |
|
| | | public static Resources getResources() {
|
| | | Context context = getAppContext();
|
| | | if (context != null) {
|
| | | return context.getResources();
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | | public static void setSplashCls(Class cls) {
|
| | | ContextHelper.splashCls = cls;
|
| | | }
|
| | |
|
| | | public static Class getSplashCls() {
|
| | | return splashCls;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 资源ID获取String
|
| | | */
|
| | | public static String getString(int stringId) {
|
| | | if (getAppContext() == null) {
|
| | | return Strings.EMPTY;
|
| | | }
|
| | | return getAppContext().getString(stringId);
|
| | | }
|
| | |
|
| | | public static String getString(int stringId, Object... formatArgs) {
|
| | | if (getAppContext() == null) {
|
| | | return Strings.EMPTY;
|
| | | }
|
| | | return getAppContext().getString(stringId, formatArgs);
|
| | | }
|
| | |
|
| | |
|
| | | public static int getDimensionPixelSize(int dimenId) {
|
| | | try {
|
| | | return getResources().getDimensionPixelSize(dimenId);
|
| | | } catch (Resources.NotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | | public static int getDimen(int dimenId) {
|
| | | try {
|
| | | return getResources().getDimensionPixelSize(dimenId);
|
| | | } catch (Resources.NotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取应用的版本号
|
| | | */
|
| | | public static String getAppVersion() {
|
| | | Context context = getAppContext();
|
| | | if (context != null) {
|
| | | PackageManager packageManager = context.getPackageManager();
|
| | | PackageInfo packageInfo;
|
| | | try {
|
| | | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
| | | return packageInfo.versionName;
|
| | | } catch (PackageManager.NameNotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | return Strings.EMPTY;
|
| | | }
|
| | |
|
| | | public static boolean isUsable(Context context) {
|
| | | if (context == null) {
|
| | | return false;
|
| | | }
|
| | |
|
| | | if (context instanceof Activity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
| | | return !((Activity) context).isDestroyed();
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | public static void startAppSetting(){
|
| | | if(getAppContext()!=null) {
|
| | | Intent intent = new Intent("android.settings.SETTINGS");
|
| | | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
| | | getAppContext().startActivity(intent);
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | |
|
New file |
| | |
| | | package com.zxing;
|
| | |
|
| | | import android.app.Activity;
|
| | | import android.content.ClipboardManager;
|
| | | import android.content.Context;
|
| | | import android.content.Intent;
|
| | | import android.content.pm.PackageInfo;
|
| | | import android.content.pm.PackageManager;
|
| | | import android.content.pm.ResolveInfo;
|
| | | import android.content.res.Resources;
|
| | | import android.graphics.Point;
|
| | | import android.net.ConnectivityManager;
|
| | | import android.net.NetworkInfo;
|
| | | import android.net.Uri;
|
| | | import android.os.Build;
|
| | | import android.os.Environment;
|
| | | import android.os.StatFs;
|
| | | import android.provider.MediaStore;
|
| | | import android.provider.Settings;
|
| | | import android.telephony.TelephonyManager;
|
| | | import android.text.TextUtils;
|
| | | import android.util.DisplayMetrics;
|
| | | import android.view.WindowManager;
|
| | |
|
| | | import com.zxing.utils.Strings;
|
| | | import com.zxing.utils.Validator;
|
| | |
|
| | | import java.io.BufferedReader;
|
| | | import java.io.File;
|
| | | import java.io.FileReader;
|
| | | import java.io.IOException;
|
| | | import java.io.InputStream;
|
| | | import java.io.Reader;
|
| | | import java.util.List;
|
| | | import java.util.Locale;
|
| | |
|
| | | import static android.telephony.TelephonyManager.SIM_STATE_READY;
|
| | |
|
| | | /**
|
| | | * 用途:取设备相关信息
|
| | | */
|
| | | public class DeviceHelper {
|
| | |
|
| | | /**
|
| | | * 获取应用的版本号
|
| | | */
|
| | | public static String getAppVersion() {
|
| | | Context context = ContextHelper.getAppContext();
|
| | | if (context != null) {
|
| | | PackageManager packageManager = context.getPackageManager();
|
| | | PackageInfo packageInfo;
|
| | | try {
|
| | | packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
| | | return packageInfo.versionName;
|
| | | } catch (PackageManager.NameNotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | return Strings.EMPTY;
|
| | | }
|
| | |
|
| | | public static void ClipData(String content) {
|
| | | ClipboardManager cm = (ClipboardManager) ContextHelper.getAppContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
| | | // 将文本内容放到系统剪贴板里。
|
| | | if (cm != null) {
|
| | | cm.setText(content);
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 启动应用的设置
|
| | | */
|
| | | public static void startAppSettings(Activity activity, int requestCode) {
|
| | | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
| | | Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
| | | intent.setData(uri);
|
| | | activity.startActivityForResult(intent, requestCode);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取版本信息 versioncode
|
| | | */
|
| | | public static int getVersionCode() {
|
| | | final Context context = ContextHelper.getAppContext();
|
| | | int version = 1;
|
| | | if (context != null) {
|
| | | PackageManager packageManager = context.getPackageManager();
|
| | | PackageInfo packInfo = null;
|
| | | try {
|
| | | packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
| | | } catch (PackageManager.NameNotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | if (packInfo != null) {
|
| | | version = packInfo.versionCode;
|
| | | }
|
| | | }
|
| | | return version;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取设备的制造商
|
| | | */
|
| | | public static String getFactory() {
|
| | | return Build.MANUFACTURER;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取系统版本号
|
| | | */
|
| | | public static String getPhoneOS() {
|
| | | return "Android " + getSysVersion() + " " + Build.VERSION.RELEASE;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 版本是否在Android6.0 以上
|
| | | */
|
| | | public static boolean isOverMarshmallow() {
|
| | | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取Android API版本
|
| | | */
|
| | | public static String getSysVersion() {
|
| | | return Build.VERSION.SDK_INT + Strings.EMPTY;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取Android API版本
|
| | | */
|
| | | public static int getSysVersionInt() {
|
| | | return Build.VERSION.SDK_INT;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取手机型号
|
| | | */
|
| | | public static String getPhoneModel() {
|
| | | String model = Build.BRAND + " " + Build.MODEL;
|
| | | if (!TextUtils.isEmpty(model) && model.length() > 50) {
|
| | | model = model.substring(0, 49);
|
| | | }
|
| | | return Validator.replaceHanzi(model);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断IMEI是否为纯数字串
|
| | | */
|
| | | private static boolean isNumber(String str) {
|
| | | if (TextUtils.isEmpty(str)) {
|
| | | return false;
|
| | | }
|
| | | boolean isNumber = true;
|
| | | int i;
|
| | | char c;
|
| | | for (i = 0; i < str.length(); i++) {
|
| | | c = str.charAt(i);
|
| | | if (!((c >= '0') && (c <= '9')) || "000000000000000".equals(str) || "0".equals(str)) {
|
| | | isNumber = false;
|
| | | break;
|
| | | }
|
| | | }
|
| | | return isNumber;
|
| | | }
|
| | |
|
| | | private static String loadFileAsString(String fileName) throws Exception {
|
| | | FileReader reader = new FileReader(fileName);
|
| | | String text = loadReaderAsString(reader);
|
| | | reader.close();
|
| | | return text;
|
| | | }
|
| | |
|
| | | private static String loadReaderAsString(Reader reader) throws Exception {
|
| | | StringBuilder builder = new StringBuilder();
|
| | | char[] buffer = new char[4096];
|
| | | int readLength = reader.read(buffer);
|
| | | while (readLength >= 0) {
|
| | | builder.append(buffer, 0, readLength);
|
| | | readLength = reader.read(buffer);
|
| | | }
|
| | | return builder.toString();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断mac地址是否合法
|
| | | */
|
| | | private static boolean isCorrectMacAddress(String address) {
|
| | | boolean flag = false;
|
| | | if (!TextUtils.isEmpty(address) && address.length() == 17) {
|
| | | address = address.replaceAll(":", Strings.EMPTY);
|
| | | flag = isHex(address);
|
| | | }
|
| | | return flag;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断是否为纯16进制数字串
|
| | | */
|
| | | private static boolean isHex(String str) {
|
| | | boolean isHexFlg = true;
|
| | | int i;
|
| | | char c;
|
| | | for (i = 0; i < str.length(); i++) {
|
| | | c = str.charAt(i);
|
| | | if (!(((c >= '0') && (c <= '9')) ||
|
| | | ((c >= 'A') && (c <= 'F')) ||
|
| | | (c >= 'a') && (c <= 'f'))) {
|
| | | isHexFlg = false;
|
| | | break;
|
| | | }
|
| | | }
|
| | | return isHexFlg;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断系统中是否存在可以启动的相机应用
|
| | | *
|
| | | * @return 存在返回true,不存在返回false
|
| | | */
|
| | | public static boolean hasCamera(Context context) {
|
| | | PackageManager packageManager = context.getPackageManager();
|
| | | Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
| | | List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
| | | return list.size() > 0;
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 检测系统是否为MIUI
|
| | | */
|
| | | private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
|
| | | private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
|
| | | private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";
|
| | |
|
| | | /**
|
| | | * 获取渠道
|
| | | */
|
| | | public static String getChannel() {
|
| | | return "";
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取手机宽高
|
| | | */
|
| | | public static String getPhonePixels(Activity activity) {
|
| | | if (activity != null) {
|
| | | DisplayMetrics dm = new DisplayMetrics();
|
| | | activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
| | | int widthPixels = dm.widthPixels;
|
| | | int heightPixels = dm.heightPixels;
|
| | | return widthPixels + "-" + heightPixels;
|
| | | }
|
| | | return "0-0";
|
| | | }
|
| | |
|
| | | /**
|
| | | * x
|
| | | * 屏幕宽度
|
| | | */
|
| | | public static int getDeviceWidth(Context context) {
|
| | | if (context != null) {
|
| | | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
| | | if (wm != null) {
|
| | | Point p = new Point();
|
| | | wm.getDefaultDisplay().getSize(p);
|
| | | return p.x;
|
| | | }
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 屏幕宽度
|
| | | */
|
| | | public static int getDeviceWidth(Activity activity) {
|
| | | if (activity != null) {
|
| | | DisplayMetrics dm = new DisplayMetrics();
|
| | | activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
| | | return dm.widthPixels;
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 屏幕高度
|
| | | */
|
| | | public static int getDeviceHeight(Activity activity) {
|
| | | if (activity != null) {
|
| | | DisplayMetrics dm = new DisplayMetrics();
|
| | | activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
| | | return dm.heightPixels;
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 判断当前有没有网络连接
|
| | | */
|
| | | public static boolean getNetworkState() {
|
| | | Context context = ContextHelper.getAppContext();
|
| | | if (context != null) {
|
| | | ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
| | | NetworkInfo networkinfo = manager.getActiveNetworkInfo();
|
| | | return !(networkinfo == null || !networkinfo.isAvailable());
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * SD卡是否挂载
|
| | | */
|
| | | public static boolean mountedSdCard() {
|
| | | return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 检测应用是否安装
|
| | | **/
|
| | | public static boolean isApkInstalled(String packageName) {
|
| | | Context context = ContextHelper.getAppContext();
|
| | | if (context != null) {
|
| | | final PackageManager packageManager = context.getPackageManager();
|
| | | List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
|
| | | for (int i = 0; i < pinfo.size(); i++) {
|
| | | if (pinfo.get(i).packageName.equalsIgnoreCase(packageName)) {
|
| | | return true;
|
| | | }
|
| | | }
|
| | | return false;
|
| | | }
|
| | | return false;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 打电话
|
| | | */
|
| | | public static void callPhone(Activity activity, String phone) {
|
| | | Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phone));
|
| | | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
| | | activity.startActivity(intent);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 调用系统发送短信
|
| | | */
|
| | | public static void sendSMSView(Activity activity, String phone, String sms) {
|
| | | Uri smsToUri = Uri.parse("smsto:" + phone);
|
| | | Intent sendIntent = new Intent(Intent.ACTION_SENDTO, smsToUri);
|
| | | sendIntent.putExtra("sms_body", sms);
|
| | | activity.startActivity(sendIntent);
|
| | | }
|
| | |
|
| | | private static TelephonyManager getTelManager() {
|
| | | Context context = ContextHelper.getAppContext();
|
| | | if (context == null) {
|
| | | return null;
|
| | | }
|
| | | return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取ISO国家码,相当于提供SIM卡的国家码
|
| | | */
|
| | | public static String getSimCountryIso() {
|
| | | if (getTelManager() != null) {
|
| | | return getTelManager().getSimCountryIso();
|
| | | }
|
| | |
|
| | | return Strings.EMPTY;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取运营商名称
|
| | | */
|
| | | public static String getSimOperatorName() {
|
| | | if (getTelManager() != null && SIM_STATE_READY == getTelManager().getSimState()) {
|
| | | return getTelManager().getSimOperatorName();
|
| | | }
|
| | |
|
| | | return Strings.EMPTY;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取系统运行内存大小 单位KB
|
| | | */
|
| | | public static long getTotalMemory() {
|
| | | String str1 = "/proc/meminfo";// 系统内存信息文件
|
| | | String str2;
|
| | | String[] arrayOfString;
|
| | | long initial_memory = 0;
|
| | |
|
| | | try {
|
| | | FileReader localFileReader = new FileReader(str1);
|
| | | BufferedReader localBufferedReader = new BufferedReader(
|
| | | localFileReader, 8192);
|
| | | str2 = localBufferedReader.readLine();// 读取meminfo第一行,系统总内存大小
|
| | | if (TextUtils.isEmpty(str2)) {
|
| | | arrayOfString = str2.split("\\s+");
|
| | | initial_memory = Integer.valueOf(arrayOfString[1]);// 获得系统总内存,单位是KB
|
| | | }
|
| | | localBufferedReader.close();
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return initial_memory;// Byte转换为KB或者MB,内存大小规格化
|
| | | }
|
| | |
|
| | | /**
|
| | | * 设备语言编码
|
| | | */
|
| | | public static String getLanguage() {
|
| | | String language = Strings.EMPTY;
|
| | | Resources resources = ContextHelper.getResources();
|
| | | if (resources != null) {
|
| | | Locale locale = ContextHelper.getResources().getConfiguration().locale;
|
| | | language = locale.getLanguage();
|
| | | }
|
| | |
|
| | | return language;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取机身总存储(不包含SD卡)
|
| | | */
|
| | | public static long getRomMemory() {
|
| | | long[] romInfo = new long[1];
|
| | | File path = Environment.getDataDirectory();
|
| | | StatFs stat = new StatFs(path.getPath());
|
| | | long blockSize = stat.getBlockSize();
|
| | | long totalBlocks = stat.getBlockCount();
|
| | | //Total rom memory
|
| | | romInfo[0] = blockSize * totalBlocks;
|
| | | return romInfo[0];
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取CPU最大频率(单位KHZ)
|
| | | */
|
| | | public static String getMaxCpuFreq() {
|
| | | StringBuilder result = new StringBuilder(Strings.EMPTY);
|
| | | ProcessBuilder cmd;
|
| | | try {
|
| | | String[] args = {"/system/bin/cat",
|
| | | "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"};
|
| | | cmd = new ProcessBuilder(args);
|
| | | Process process = cmd.start();
|
| | | InputStream in = process.getInputStream();
|
| | | byte[] re = new byte[24];
|
| | | while (in.read(re) != -1) {
|
| | | result.append(new String(re));
|
| | | }
|
| | | in.close();
|
| | | } catch (IOException ex) {
|
| | | ex.printStackTrace();
|
| | | result = new StringBuilder("N/A");
|
| | | }
|
| | | return result.toString().trim();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 是否取到所有信息
|
| | | */
|
| | | private static boolean isGetSuccess() {
|
| | | return !TextUtils.isEmpty(getPhoneModel()) && !TextUtils.isEmpty(getFactory())
|
| | | && !TextUtils.isEmpty(getMaxCpuFreq()) && getRomMemory() > 0 && getTotalMemory() > 0;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.zxing;
|
| | |
|
| | | /**
|
| | | * 跨模块参数定义
|
| | | * 注意:此类仅仅用于存放跨模块的参数,模块内的参数请放在模块的constants理
|
| | | * <p>
|
| | | *
|
| | | */
|
| | | public interface Extras {
|
| | |
|
| | | interface device{
|
| | | String EXREAS_CHANNEL = "extra_channel";
|
| | | String EXTRAS_DEVICE_DISPATCH = "extra_device_dispatch";
|
| | | String EXTRAS_FROM_DISPATCH = "extra_from_dispatch";
|
| | | }
|
| | |
|
| | | interface enterprise {
|
| | | String cropId = "extra_crop_id";
|
| | | String appId = "extra_app_id";
|
| | | String deptId = "extra_dept_id";
|
| | | String CREATE_DEPT = "extra_create_dept";//组织架构创建部门
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * from
|
| | | **/
|
| | | interface FROM {
|
| | | String FROM = "from";
|
| | | String SCAN_BACK = "scan";
|
| | | String H5_SCAN_JUMP = "scan_jump";
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing;
|
| | |
|
| | | import android.content.Intent;
|
| | | import android.graphics.Rect;
|
| | | import android.os.Bundle;
|
| | | import android.os.Handler;
|
| | |
|
| | | import com.google.zxing.Result;
|
| | | import com.zxing.camera.CameraManager;
|
| | |
|
| | |
|
| | | public interface IZxingActivity {
|
| | | Handler getHandler();
|
| | |
|
| | | void startActivity(Intent intent);
|
| | |
|
| | | void setResult(int code, Intent intent);
|
| | |
|
| | | void finish();
|
| | |
|
| | | void drawViewfinder();
|
| | |
|
| | | CameraManager getCameraManager();
|
| | |
|
| | | Rect getCropRect();
|
| | |
|
| | | void handleDecode(Result obj, Bundle bundle);
|
| | | }
|
New file |
| | |
| | | package com.zxing.camera;
|
| | |
|
| | | import android.annotation.SuppressLint;
|
| | | import android.content.Context;
|
| | | import android.hardware.Camera;
|
| | | import android.os.AsyncTask;
|
| | | import android.util.Log;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.Collection;
|
| | | import java.util.concurrent.RejectedExecutionException;
|
| | |
|
| | | public class AutoFocusManager implements Camera.AutoFocusCallback {
|
| | |
|
| | | private static final String TAG = AutoFocusManager.class.getSimpleName();
|
| | |
|
| | | private static final long AUTO_FOCUS_INTERVAL_MS = 1800L;
|
| | | private static final Collection<String> FOCUS_MODES_CALLING_AF;
|
| | |
|
| | | static {
|
| | | FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
|
| | | FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
|
| | | FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
|
| | | }
|
| | |
|
| | | private final boolean useAutoFocus;
|
| | | private final Camera camera;
|
| | | private boolean stopped;
|
| | | private boolean focusing;
|
| | | private AsyncTask<?, ?, ?> outstandingTask;
|
| | |
|
| | | public AutoFocusManager(Context context, Camera camera) {
|
| | | this.camera = camera;
|
| | | String currentFocusMode = camera.getParameters().getFocusMode();
|
| | | useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
|
| | | Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
|
| | | start();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public synchronized void onAutoFocus(boolean success, Camera theCamera) {
|
| | | focusing = false;
|
| | | autoFocusAgainLater();
|
| | | }
|
| | |
|
| | | @SuppressLint("NewApi")
|
| | | private synchronized void autoFocusAgainLater() {
|
| | | if (!stopped && outstandingTask == null) {
|
| | | AutoFocusTask newTask = new AutoFocusTask();
|
| | | try {
|
| | | newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
| | | outstandingTask = newTask;
|
| | | } catch (RejectedExecutionException ree) {
|
| | | Log.w(TAG, "Could not request auto focus", ree);
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void start() {
|
| | | if (useAutoFocus) {
|
| | | outstandingTask = null;
|
| | | if (!stopped && !focusing) {
|
| | | try {
|
| | | camera.autoFocus(this);
|
| | | focusing = true;
|
| | | } catch (RuntimeException re) {
|
| | | // Have heard RuntimeException reported in Android 4.0.x+;
|
| | | // continue?
|
| | | Log.w(TAG, "Unexpected exception while focusing", re);
|
| | | // Try again later to keep cycle going
|
| | | autoFocusAgainLater();
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | private synchronized void cancelOutstandingTask() {
|
| | | if (outstandingTask != null) {
|
| | | if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
|
| | | outstandingTask.cancel(true);
|
| | | }
|
| | | outstandingTask = null;
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void stop() {
|
| | | stopped = true;
|
| | | if (useAutoFocus) {
|
| | | cancelOutstandingTask();
|
| | | // Doesn't hurt to call this even if not focusing
|
| | | try {
|
| | | camera.cancelAutoFocus();
|
| | | } catch (RuntimeException re) {
|
| | | // Have heard RuntimeException reported in Android 4.0.x+;
|
| | | // continue?
|
| | | Log.w(TAG, "Unexpected exception while cancelling focusing", re);
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | private final class AutoFocusTask extends AsyncTask<Object, Object, Object> {
|
| | | @Override
|
| | | protected Object doInBackground(Object... voids) {
|
| | | try {
|
| | | Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
|
| | | } catch (InterruptedException e) {
|
| | | // continue
|
| | | }
|
| | | start();
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.camera;
|
| | |
|
| | | import android.annotation.SuppressLint;
|
| | | import android.content.Context;
|
| | | import android.graphics.Point;
|
| | | import android.hardware.Camera;
|
| | | import android.util.Log;
|
| | | import android.view.Display;
|
| | | import android.view.WindowManager;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.Collections;
|
| | | import java.util.Comparator;
|
| | | import java.util.Iterator;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * 描述: 该类主要负责设置相机的参数信息,获取最佳的预览界面
|
| | | */
|
| | | public final class CameraConfigurationManager {
|
| | |
|
| | | private static final String TAG = "CameraConfiguration";
|
| | |
|
| | | private static final int MIN_PREVIEW_PIXELS = 480 * 320;
|
| | | private static final double MAX_ASPECT_DISTORTION = 0.15;
|
| | |
|
| | | private final Context context;
|
| | |
|
| | | // 屏幕分辨率
|
| | | private Point screenResolution;
|
| | | // 相机分辨率
|
| | | private Point cameraResolution;
|
| | |
|
| | | public CameraConfigurationManager(Context context) {
|
| | | this.context = context;
|
| | | }
|
| | |
|
| | | public void initFromCameraParameters(Camera camera) {
|
| | | Camera.Parameters parameters = camera.getParameters();
|
| | | WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
| | | Display display = manager.getDefaultDisplay();
|
| | | Point theScreenResolution = new Point();
|
| | | theScreenResolution = getDisplaySize(display);
|
| | |
|
| | | screenResolution = theScreenResolution;
|
| | | Log.i(TAG, "Screen resolution: " + screenResolution);
|
| | |
|
| | | /** 因为换成了竖屏显示,所以不替换屏幕宽高得出的预览图是变形的 */
|
| | | Point screenResolutionForCamera = new Point();
|
| | | screenResolutionForCamera.x = screenResolution.x;
|
| | | screenResolutionForCamera.y = screenResolution.y;
|
| | |
|
| | | if (screenResolution.x < screenResolution.y) {
|
| | | screenResolutionForCamera.x = screenResolution.y;
|
| | | screenResolutionForCamera.y = screenResolution.x;
|
| | | }
|
| | |
|
| | | cameraResolution = findBestPreviewSizeValue(parameters, screenResolutionForCamera);
|
| | | Log.i(TAG, "Camera resolution x: " + cameraResolution.x);
|
| | | Log.i(TAG, "Camera resolution y: " + cameraResolution.y);
|
| | | }
|
| | |
|
| | | @SuppressWarnings("deprecation")
|
| | | @SuppressLint("NewApi")
|
| | | private Point getDisplaySize(final Display display) {
|
| | | final Point point = new Point();
|
| | | try {
|
| | | display.getSize(point);
|
| | | } catch (NoSuchMethodError ignore) {
|
| | | point.x = display.getWidth();
|
| | | point.y = display.getHeight();
|
| | | }
|
| | | return point;
|
| | | }
|
| | |
|
| | | public void setDesiredCameraParameters(Camera camera, boolean safeMode) {
|
| | | Camera.Parameters parameters = camera.getParameters();
|
| | |
|
| | | if (parameters == null) {
|
| | | Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
|
| | | return;
|
| | | }
|
| | |
|
| | | Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
|
| | |
|
| | | if (safeMode) {
|
| | | Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
|
| | | }
|
| | |
|
| | | parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
|
| | | camera.setParameters(parameters);
|
| | |
|
| | | Camera.Parameters afterParameters = camera.getParameters();
|
| | | Camera.Size afterSize = afterParameters.getPreviewSize();
|
| | | if (afterSize != null && (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize
|
| | | .height)) {
|
| | | Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x' +
|
| | | cameraResolution.y + ", but after setting it, preview size is " + afterSize.width + 'x'
|
| | | + afterSize.height);
|
| | | cameraResolution.x = afterSize.width;
|
| | | cameraResolution.y = afterSize.height;
|
| | | }
|
| | |
|
| | | /** 设置相机预览为竖屏 */
|
| | | camera.setDisplayOrientation(90);
|
| | | }
|
| | |
|
| | | public Point getCameraResolution() {
|
| | | return cameraResolution;
|
| | | }
|
| | |
|
| | | public Point getScreenResolution() {
|
| | | return screenResolution;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 从相机支持的分辨率中计算出最适合的预览界面尺寸
|
| | | *
|
| | | * @param parameters
|
| | | * @param screenResolution
|
| | | * @return
|
| | | */
|
| | | private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
|
| | | List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
|
| | | if (rawSupportedSizes == null) {
|
| | | Log.w(TAG, "Device returned no supported preview sizes; using default");
|
| | | Camera.Size defaultSize = parameters.getPreviewSize();
|
| | | return new Point(defaultSize.width, defaultSize.height);
|
| | | }
|
| | |
|
| | | // Sort by size, descending
|
| | | List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
|
| | | Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
|
| | | @Override
|
| | | public int compare(Camera.Size a, Camera.Size b) {
|
| | | int aPixels = a.height * a.width;
|
| | | int bPixels = b.height * b.width;
|
| | | if (bPixels < aPixels) {
|
| | | return -1;
|
| | | }
|
| | | if (bPixels > aPixels) {
|
| | | return 1;
|
| | | }
|
| | | return 0;
|
| | | }
|
| | | });
|
| | |
|
| | | if (Log.isLoggable(TAG, Log.INFO)) {
|
| | | StringBuilder previewSizesString = new StringBuilder();
|
| | | for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
|
| | | previewSizesString.append(supportedPreviewSize.width).append('x').append
|
| | | (supportedPreviewSize.height).append(' ');
|
| | | }
|
| | | Log.i(TAG, "Supported preview sizes: " + previewSizesString);
|
| | | }
|
| | |
|
| | | double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
|
| | |
|
| | | // Remove sizes that are unsuitable
|
| | | Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
|
| | | while (it.hasNext()) {
|
| | | Camera.Size supportedPreviewSize = it.next();
|
| | | int realWidth = supportedPreviewSize.width;
|
| | | int realHeight = supportedPreviewSize.height;
|
| | | if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
|
| | | it.remove();
|
| | | continue;
|
| | | }
|
| | |
|
| | | boolean isCandidatePortrait = realWidth < realHeight;
|
| | | int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
|
| | | int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
|
| | |
|
| | | double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
|
| | | double distortion = Math.abs(aspectRatio - screenAspectRatio);
|
| | | if (distortion > MAX_ASPECT_DISTORTION) {
|
| | | it.remove();
|
| | | continue;
|
| | | }
|
| | |
|
| | | if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
|
| | | Point exactPoint = new Point(realWidth, realHeight);
|
| | | Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
|
| | | return exactPoint;
|
| | | }
|
| | | }
|
| | |
|
| | | // If no exact match, use largest preview size. This was not a great
|
| | | // idea on older devices because
|
| | | // of the additional computation needed. We're likely to get here on
|
| | | // newer Android 4+ devices, where
|
| | | // the CPU is much more powerful.
|
| | | if (!supportedPreviewSizes.isEmpty()) {
|
| | | Camera.Size largestPreview = supportedPreviewSizes.get(0);
|
| | | Point largestSize = new Point(largestPreview.width, largestPreview.height);
|
| | | Log.i(TAG, "Using largest suitable preview size: " + largestSize);
|
| | | return largestSize;
|
| | | }
|
| | |
|
| | | // If there is nothing at all suitable, return current preview size
|
| | | Camera.Size defaultPreview = parameters.getPreviewSize();
|
| | | Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
|
| | | Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
|
| | |
|
| | | return defaultSize;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.zxing.camera;
|
| | |
|
| | | import android.content.Context;
|
| | | import android.content.pm.FeatureInfo;
|
| | | import android.content.pm.PackageManager;
|
| | | import android.graphics.Point;
|
| | | import android.hardware.Camera;
|
| | | import android.hardware.camera2.CameraAccessException;
|
| | | import android.hardware.camera2.CameraCharacteristics;
|
| | | import android.os.Build;
|
| | | import android.os.Handler;
|
| | | import android.util.Log;
|
| | | import android.view.SurfaceHolder;
|
| | |
|
| | | import com.zxing.camera.open.OpenCameraInterface;
|
| | |
|
| | | import java.io.IOException;
|
| | |
|
| | | /**
|
| | | * This object wraps the Camera service object and expects to be the only one
|
| | | * talking to it. The implementation encapsulates the steps needed to take
|
| | | * preview-sized images, which are used for both preview and decoding.
|
| | | */
|
| | | public class CameraManager {
|
| | |
|
| | | private static final String TAG = CameraManager.class.getSimpleName();
|
| | | private boolean status = false; // 记录手机状态
|
| | | private final Context context;
|
| | | private final CameraConfigurationManager configManager;
|
| | | /**
|
| | | * Preview frames are delivered here, which we pass on to the registered
|
| | | * handler. Make sure to clear the handler so it will only receive one
|
| | | * message.
|
| | | */
|
| | | private final PreviewCallback previewCallback;
|
| | | private Camera camera;
|
| | | private AutoFocusManager autoFocusManager;
|
| | | private boolean initialized;
|
| | | private boolean previewing;
|
| | | private int requestedCameraId = -1;
|
| | |
|
| | | public CameraManager(Context context) {
|
| | | this.context = context;
|
| | | this.configManager = new CameraConfigurationManager(context);
|
| | | previewCallback = new PreviewCallback(configManager);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Opens the camera driver and initializes the hardware parameters.
|
| | | *
|
| | | * @param holder The surface object which the camera will draw preview frames
|
| | | * into.
|
| | | * @throws IOException Indicates the camera driver failed to open.
|
| | | */
|
| | | public synchronized void openDriver(SurfaceHolder holder) throws IOException {
|
| | | Camera theCamera = camera;
|
| | | if (theCamera == null) {
|
| | |
|
| | | if (requestedCameraId >= 0) {
|
| | | theCamera = OpenCameraInterface.open(requestedCameraId);
|
| | | } else {
|
| | | theCamera = OpenCameraInterface.open();
|
| | | }
|
| | |
|
| | | if (theCamera == null) {
|
| | | throw new IOException();
|
| | | }
|
| | | camera = theCamera;
|
| | | }
|
| | | theCamera.setPreviewDisplay(holder);
|
| | |
|
| | | if (!initialized) {
|
| | | initialized = true;
|
| | | configManager.initFromCameraParameters(theCamera);
|
| | | }
|
| | |
|
| | | Camera.Parameters parameters = theCamera.getParameters();
|
| | | String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save
|
| | | // these,
|
| | | // temporarily
|
| | | try {
|
| | | configManager.setDesiredCameraParameters(theCamera, false);
|
| | | } catch (RuntimeException re) {
|
| | | // Driver failed
|
| | | Log.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");
|
| | | Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);
|
| | | // Reset:
|
| | | if (parametersFlattened != null) {
|
| | | parameters = theCamera.getParameters();
|
| | | parameters.unflatten(parametersFlattened);
|
| | | try {
|
| | | theCamera.setParameters(parameters);
|
| | | configManager.setDesiredCameraParameters(theCamera, true);
|
| | | } catch (RuntimeException re2) {
|
| | | // Well, darn. Give up
|
| | | Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | public synchronized boolean isOpen() {
|
| | | return camera != null;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Closes the camera driver if still in use.
|
| | | */
|
| | | public synchronized void closeDriver() {
|
| | | if (camera != null) {
|
| | | camera.release();
|
| | | camera = null;
|
| | | // Make sure to clear these each time we close the camera, so that
|
| | | // any scanning rect
|
| | | // requested by intent is forgotten.
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Asks the camera hardware to begin drawing preview frames to the screen.
|
| | | */
|
| | | public synchronized void startPreview() {
|
| | | Camera theCamera = camera;
|
| | | if (theCamera != null && !previewing) {
|
| | | theCamera.startPreview();
|
| | | previewing = true;
|
| | | autoFocusManager = new AutoFocusManager(context, camera);
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Tells the camera to stop drawing preview frames.
|
| | | */
|
| | | public synchronized void stopPreview() {
|
| | | if (autoFocusManager != null) {
|
| | | autoFocusManager.stop();
|
| | | autoFocusManager = null;
|
| | | }
|
| | | if (camera != null && previewing) {
|
| | | camera.stopPreview();
|
| | | previewCallback.setHandler(null, 0);
|
| | | previewing = false;
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * A single preview frame will be returned to the handler supplied. The data
|
| | | * will arrive as byte[] in the message.obj field, with width and height
|
| | | * encoded as message.arg1 and message.arg2, respectively.
|
| | | *
|
| | | * @param handler The handler to send the message to.
|
| | | * @param message The what field of the message to be sent.
|
| | | */
|
| | | public synchronized void requestPreviewFrame(Handler handler, int message) {
|
| | | Camera theCamera = camera;
|
| | | if (theCamera != null && previewing) {
|
| | | previewCallback.setHandler(handler, message);
|
| | | theCamera.setOneShotPreviewCallback(previewCallback);
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Allows third party apps to specify the camera ID, rather than determine
|
| | | * it automatically based on available cameras and their orientation.
|
| | | *
|
| | | * @param cameraId camera ID of the camera to use. A negative value means
|
| | | * "no preference".
|
| | | */
|
| | | public synchronized void setManualCameraId(int cameraId) {
|
| | | requestedCameraId = cameraId;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取相机分辨率
|
| | | *
|
| | | * @return
|
| | | */
|
| | | public Point getCameraResolution() {
|
| | | return configManager.getCameraResolution();
|
| | | }
|
| | |
|
| | | public Camera.Size getPreviewSize() {
|
| | | if (null != camera) {
|
| | | return camera.getParameters().getPreviewSize();
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | | public Camera getCamera() {
|
| | | return camera;
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.camera;
|
| | |
|
| | | import android.graphics.Point;
|
| | | import android.hardware.Camera;
|
| | | import android.os.Handler;
|
| | | import android.os.Message;
|
| | | import android.util.Log;
|
| | |
|
| | | public class PreviewCallback implements Camera.PreviewCallback {
|
| | |
|
| | | private static final String TAG = PreviewCallback.class.getSimpleName();
|
| | |
|
| | | private final CameraConfigurationManager configManager;
|
| | | private Handler previewHandler;
|
| | | private int previewMessage;
|
| | |
|
| | | public PreviewCallback(CameraConfigurationManager configManager) {
|
| | | this.configManager = configManager;
|
| | | }
|
| | |
|
| | | public void setHandler(Handler previewHandler, int previewMessage) {
|
| | | this.previewHandler = previewHandler;
|
| | | this.previewMessage = previewMessage;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onPreviewFrame(byte[] data, Camera camera) {
|
| | | Point cameraResolution = configManager.getCameraResolution();
|
| | | Handler thePreviewHandler = previewHandler;
|
| | | if (cameraResolution != null && thePreviewHandler != null) {
|
| | | Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
|
| | | cameraResolution.y, data);
|
| | | message.sendToTarget();
|
| | | previewHandler = null;
|
| | | } else {
|
| | | Log.d(TAG, "Got preview callback, but no handler or resolution available");
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.camera.open;
|
| | |
|
| | | import android.hardware.Camera;
|
| | | import android.util.Log;
|
| | |
|
| | | public class OpenCameraInterface {
|
| | |
|
| | | private static final String TAG = OpenCameraInterface.class.getName();
|
| | |
|
| | | /**
|
| | | * Opens the requested camera with {@link Camera#open(int)}, if one exists.
|
| | | *
|
| | | * @param cameraId camera ID of the camera to use. A negative value means
|
| | | * "no preference"
|
| | | * @return handle to {@link Camera} that was opened
|
| | | */
|
| | | public static Camera open(int cameraId) {
|
| | |
|
| | | int numCameras = Camera.getNumberOfCameras();
|
| | | if (numCameras == 0) {
|
| | | Log.w(TAG, "No cameras!");
|
| | | return null;
|
| | | }
|
| | |
|
| | | boolean explicitRequest = cameraId >= 0;
|
| | |
|
| | | if (!explicitRequest) {
|
| | | // Select a camera if no explicit camera requested
|
| | | int index = 0;
|
| | | while (index < numCameras) {
|
| | | Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
| | | Camera.getCameraInfo(index, cameraInfo);
|
| | | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
|
| | | break;
|
| | | }
|
| | | index++;
|
| | | }
|
| | |
|
| | | cameraId = index;
|
| | | }
|
| | |
|
| | | Camera camera;
|
| | | if (cameraId < numCameras) {
|
| | | Log.i(TAG, "Opening camera #" + cameraId);
|
| | | camera = Camera.open(cameraId);
|
| | | } else {
|
| | | if (explicitRequest) {
|
| | | Log.w(TAG, "Requested camera does not exist: " + cameraId);
|
| | | camera = null;
|
| | | } else {
|
| | | Log.i(TAG, "No camera facing back; returning camera #0");
|
| | | camera = Camera.open(0);
|
| | | }
|
| | | }
|
| | |
|
| | | return camera;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Opens a rear-facing camera with {@link Camera#open(int)}, if one exists,
|
| | | * or opens camera 0.
|
| | | *
|
| | | * @return handle to {@link Camera} that was opened
|
| | | */
|
| | | public static Camera open() {
|
| | | return open(-1);
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.decode;
|
| | |
|
| | | import com.google.zxing.BarcodeFormat;
|
| | |
|
| | | import java.util.Collection;
|
| | | import java.util.EnumSet;
|
| | | import java.util.Set;
|
| | |
|
| | | public class DecodeFormatManager {
|
| | |
|
| | | // 1D解码
|
| | | private static final Set<BarcodeFormat> PRODUCT_FORMATS;
|
| | | private static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
|
| | | private static final Set<BarcodeFormat> ONE_D_FORMATS;
|
| | |
|
| | | // 二维码解码
|
| | | private static final Set<BarcodeFormat> QR_CODE_FORMATS;
|
| | |
|
| | | static {
|
| | | PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A, BarcodeFormat.UPC_E, BarcodeFormat.EAN_13,
|
| | | BarcodeFormat.EAN_8, BarcodeFormat.RSS_14, BarcodeFormat.RSS_EXPANDED);
|
| | | INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39, BarcodeFormat.CODE_93, BarcodeFormat
|
| | | .CODE_128, BarcodeFormat.ITF, BarcodeFormat.CODABAR);
|
| | | ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);
|
| | | ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);
|
| | |
|
| | | QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
|
| | | }
|
| | |
|
| | | public static Collection<BarcodeFormat> getQrCodeFormats() {
|
| | | return QR_CODE_FORMATS;
|
| | | }
|
| | |
|
| | | public static Collection<BarcodeFormat> getBarCodeFormats() {
|
| | | return ONE_D_FORMATS;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.zxing.decode;
|
| | |
|
| | | import android.graphics.Bitmap;
|
| | | import android.graphics.Rect;
|
| | | import android.hardware.Camera.Size;
|
| | | import android.os.Bundle;
|
| | | import android.os.Handler;
|
| | | import android.os.Looper;
|
| | | import android.os.Message;
|
| | |
|
| | | import com.google.zxing.BinaryBitmap;
|
| | | import com.google.zxing.DecodeHintType;
|
| | | import com.google.zxing.MultiFormatReader;
|
| | | import com.google.zxing.PlanarYUVLuminanceSource;
|
| | | import com.google.zxing.ReaderException;
|
| | | import com.google.zxing.Result;
|
| | | import com.google.zxing.common.HybridBinarizer;
|
| | | import com.zxing.IZxingActivity;
|
| | | import com.zxing.R;
|
| | |
|
| | | import java.io.ByteArrayOutputStream;
|
| | | import java.util.Map;
|
| | |
|
| | | public class DecodeHandler extends Handler {
|
| | |
|
| | | private final IZxingActivity activity;
|
| | | private final MultiFormatReader multiFormatReader;
|
| | | private boolean running = true;
|
| | |
|
| | | public DecodeHandler(IZxingActivity activity, Map<DecodeHintType, Object> hints) {
|
| | | multiFormatReader = new MultiFormatReader();
|
| | | multiFormatReader.setHints(hints);
|
| | | this.activity = activity;
|
| | | }
|
| | |
|
| | | private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
|
| | | int[] pixels = source.renderThumbnail();
|
| | | int width = source.getThumbnailWidth();
|
| | | int height = source.getThumbnailHeight();
|
| | | Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
|
| | | ByteArrayOutputStream out = new ByteArrayOutputStream();
|
| | | bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
|
| | | bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void handleMessage(Message message) {
|
| | | if (!running) {
|
| | | return;
|
| | | }
|
| | | if (message.what == R.id.decode) {
|
| | | decode((byte[]) message.obj, message.arg1, message.arg2);
|
| | |
|
| | | } else if (message.what == R.id.quit) {
|
| | | running = false;
|
| | | Looper.myLooper().quit();
|
| | |
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Decode the data within the viewfinder rectangle, and time how long it
|
| | | * took. For efficiency, reuse the same reader objects from one decode to
|
| | | * the next.
|
| | | *
|
| | | * @param data The YUV preview frame.
|
| | | * @param width The width of the preview frame.
|
| | | * @param height The height of the preview frame.
|
| | | */
|
| | | private void decode(byte[] data, int width, int height) {
|
| | | Size size = activity.getCameraManager().getPreviewSize();
|
| | |
|
| | | // 这里需要将获取的data翻转一下,因为相机默认拿的的横屏的数据
|
| | | byte[] rotatedData = new byte[data.length];
|
| | | for (int y = 0; y < size.height; y++) {
|
| | | for (int x = 0; x < size.width; x++)
|
| | | rotatedData[x * size.height + size.height - y - 1] = data[x + y * size.width];
|
| | | }
|
| | |
|
| | | // 宽高也要调整
|
| | | int tmp = size.width;
|
| | | size.width = size.height;
|
| | | size.height = tmp;
|
| | |
|
| | | Result rawResult = null;
|
| | | PlanarYUVLuminanceSource source = buildLuminanceSource(rotatedData, size.width, size.height);
|
| | | if (source != null) {
|
| | | BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
| | | try {
|
| | | rawResult = multiFormatReader.decodeWithState(bitmap);
|
| | | } catch (ReaderException re) {
|
| | | // continue
|
| | | } finally {
|
| | | multiFormatReader.reset();
|
| | | }
|
| | | }
|
| | |
|
| | | Handler handler = activity.getHandler();
|
| | | if (rawResult != null) {
|
| | | // Don't log the barcode contents for security.
|
| | | if (handler != null) {
|
| | | Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
|
| | | Bundle bundle = new Bundle();
|
| | | bundleThumbnail(source, bundle);
|
| | | message.setData(bundle);
|
| | | message.sendToTarget();
|
| | | }
|
| | | } else {
|
| | | if (handler != null) {
|
| | | Message message = Message.obtain(handler, R.id.decode_failed);
|
| | | message.sendToTarget();
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * A factory method to build the appropriate LuminanceSource object based on
|
| | | * the format of the preview buffers, as described by Camera.Parameters.
|
| | | *
|
| | | * @param data A preview frame.
|
| | | * @param width The width of the image.
|
| | | * @param height The height of the image.
|
| | | * @return A PlanarYUVLuminanceSource instance.
|
| | | */
|
| | | public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
|
| | | Rect rect = activity.getCropRect();
|
| | | if (rect == null) {
|
| | | return null;
|
| | | }
|
| | | // Go ahead and assume it's YUV rather than die.
|
| | | return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect
|
| | | .height(), false);
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.decode;
|
| | |
|
| | | import android.os.Handler;
|
| | | import android.os.Looper;
|
| | |
|
| | | import com.google.zxing.BarcodeFormat;
|
| | | import com.google.zxing.DecodeHintType;
|
| | | import com.zxing.IZxingActivity;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.Collection;
|
| | | import java.util.EnumMap;
|
| | | import java.util.EnumSet;
|
| | | import java.util.Map;
|
| | | import java.util.concurrent.CountDownLatch;
|
| | |
|
| | | /**
|
| | | * This thread does all the heavy lifting of decoding the images.
|
| | | */
|
| | | public class DecodeThread extends Thread {
|
| | |
|
| | | public static final String BARCODE_BITMAP = "barcode_bitmap";
|
| | |
|
| | | public static final int BARCODE_MODE = 0X100;
|
| | | public static final int QRCODE_MODE = 0X200;
|
| | | public static final int ALL_MODE = 0X300;
|
| | |
|
| | | private final IZxingActivity activity;
|
| | | private final Map<DecodeHintType, Object> hints;
|
| | | private final CountDownLatch handlerInitLatch;
|
| | | private Handler handler;
|
| | |
|
| | | public DecodeThread(IZxingActivity activity, int decodeMode) {
|
| | |
|
| | | this.activity = activity;
|
| | | handlerInitLatch = new CountDownLatch(1);
|
| | |
|
| | | hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
|
| | |
|
| | | Collection<BarcodeFormat> decodeFormats = new ArrayList<BarcodeFormat>();
|
| | | decodeFormats.addAll(EnumSet.of(BarcodeFormat.AZTEC));
|
| | | decodeFormats.addAll(EnumSet.of(BarcodeFormat.PDF_417));
|
| | |
|
| | | switch (decodeMode) {
|
| | | case BARCODE_MODE:
|
| | | decodeFormats.addAll(DecodeFormatManager.getBarCodeFormats());
|
| | | break;
|
| | |
|
| | | case QRCODE_MODE:
|
| | | decodeFormats.addAll(DecodeFormatManager.getQrCodeFormats());
|
| | | break;
|
| | |
|
| | | case ALL_MODE:
|
| | | decodeFormats.addAll(DecodeFormatManager.getBarCodeFormats());
|
| | | decodeFormats.addAll(DecodeFormatManager.getQrCodeFormats());
|
| | | break;
|
| | |
|
| | | default:
|
| | | break;
|
| | | }
|
| | |
|
| | | hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
|
| | | }
|
| | |
|
| | | public Handler getHandler() {
|
| | | try {
|
| | | handlerInitLatch.await();
|
| | | } catch (InterruptedException ie) {
|
| | | // continue?
|
| | | }
|
| | | return handler;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void run() {
|
| | | Looper.prepare();
|
| | | handler = new DecodeHandler(activity, hints);
|
| | | handlerInitLatch.countDown();
|
| | | Looper.loop();
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.decode;
|
| | |
|
| | | import android.graphics.Bitmap;
|
| | | import android.graphics.BitmapFactory;
|
| | |
|
| | | import com.google.zxing.LuminanceSource;
|
| | |
|
| | | import java.io.FileNotFoundException;
|
| | |
|
| | | public class RGBLuminanceSource extends LuminanceSource {
|
| | | private final byte[] luminances;
|
| | |
|
| | | public RGBLuminanceSource(String path) throws FileNotFoundException {
|
| | | this(loadBitmap(path));
|
| | | }
|
| | |
|
| | | public RGBLuminanceSource(Bitmap bitmap) {
|
| | | super(bitmap.getWidth(), bitmap.getHeight());
|
| | | int width = bitmap.getWidth();
|
| | | int height = bitmap.getHeight();
|
| | | int[] pixels = new int[width * height];
|
| | | bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
| | | // In order to measure pure decoding speed, we convert the entire image
|
| | | // to a greyscale array
|
| | | // up front, which is the same as the Y channel of the
|
| | | // YUVLuminanceSource in the real app.
|
| | | luminances = new byte[width * height];
|
| | | for (int y = 0; y < height; y++) {
|
| | | int offset = y * width;
|
| | | for (int x = 0; x < width; x++) {
|
| | | int pixel = pixels[offset + x];
|
| | | int r = (pixel >> 16) & 0xff;
|
| | | int g = (pixel >> 8) & 0xff;
|
| | | int b = pixel & 0xff;
|
| | | if (r == g && g == b) {
|
| | | // Image is already greyscale, so pick any channel.
|
| | | luminances[offset + x] = (byte) r;
|
| | | } else {
|
| | | // Calculate luminance cheaply, favoring green.
|
| | | luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public byte[] getRow(int y, byte[] row) {
|
| | | if (y < 0 || y >= getHeight()) {
|
| | | throw new IllegalArgumentException(
|
| | | "Requested row is outside the image: " + y);
|
| | | }
|
| | | int width = getWidth();
|
| | | if (row == null || row.length < width) {
|
| | | row = new byte[width];
|
| | | }
|
| | | System.arraycopy(luminances, y * width, row, 0, width);
|
| | | return row;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public byte[] getMatrix() {
|
| | | return luminances;
|
| | | }
|
| | |
|
| | | private static Bitmap loadBitmap(String path) throws FileNotFoundException {
|
| | | Bitmap bitmap = BitmapFactory.decodeFile(path);
|
| | | if (bitmap == null) {
|
| | | throw new FileNotFoundException("Couldn't open " + path);
|
| | | }
|
| | | return bitmap;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.zxing.qrcode;///*
|
| | |
|
| | | import android.app.Activity;
|
| | | import android.content.Intent;
|
| | | import android.database.Cursor;
|
| | | import android.graphics.Rect;
|
| | | import android.net.Uri;
|
| | | import android.os.Bundle;
|
| | | import android.os.Handler;
|
| | | import android.provider.MediaStore;
|
| | | import android.text.TextUtils;
|
| | | import android.util.Log;
|
| | | import android.view.LayoutInflater;
|
| | | import android.view.SurfaceHolder;
|
| | | import android.view.SurfaceView;
|
| | | import android.view.View;
|
| | | import android.view.Window;
|
| | | import android.view.WindowManager;
|
| | | import android.view.animation.Animation;
|
| | | import android.view.animation.TranslateAnimation;
|
| | | import android.widget.ImageView;
|
| | | import android.widget.LinearLayout;
|
| | | import android.widget.RelativeLayout;
|
| | | import android.widget.Toast;
|
| | |
|
| | | import androidx.appcompat.content.res.AppCompatResources;
|
| | |
|
| | | import com.google.zxing.BinaryBitmap;
|
| | | import com.google.zxing.ChecksumException;
|
| | | import com.google.zxing.DecodeHintType;
|
| | | import com.google.zxing.FormatException;
|
| | | import com.google.zxing.NotFoundException;
|
| | | import com.google.zxing.Result;
|
| | | import com.google.zxing.common.HybridBinarizer;
|
| | | import com.google.zxing.qrcode.QRCodeReader;
|
| | | import com.zxing.IZxingActivity;
|
| | | import com.zxing.R;
|
| | | import com.zxing.camera.CameraManager;
|
| | | import com.zxing.decode.DecodeThread;
|
| | | import com.zxing.decode.RGBLuminanceSource;
|
| | | import com.zxing.utils.BeepManager;
|
| | | import com.zxing.utils.CaptureActivityHandler;
|
| | | import com.zxing.utils.InactivityTimer;
|
| | |
|
| | | import java.io.FileNotFoundException;
|
| | | import java.io.IOException;
|
| | | import java.lang.reflect.Field;
|
| | | import java.util.Hashtable;
|
| | |
|
| | | /**
|
| | | * 用途:扫一扫
|
| | | */
|
| | | public final class CaptureActivity extends Activity implements IZxingActivity, SurfaceHolder.Callback {
|
| | | private static final String TAG = CaptureActivity.class.getSimpleName();
|
| | | private final int REQUEST_CODE = 33;
|
| | | private CameraManager cameraManager;
|
| | | private CaptureActivityHandler handler;
|
| | | private InactivityTimer inactivityTimer;
|
| | | private BeepManager beepManager;
|
| | | private SurfaceView scanPreview = null;
|
| | | private RelativeLayout scanContainer;
|
| | | private RelativeLayout scanCropView;
|
| | | private LinearLayout backll;
|
| | | private ImageView light_iv;
|
| | | private Rect mCropRect = null;
|
| | | private boolean isHasSurface = false;
|
| | |
|
| | | @Override
|
| | | public Handler getHandler() {
|
| | | return handler;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void drawViewfinder() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public CameraManager getCameraManager() {
|
| | | return cameraManager;
|
| | | }
|
| | |
|
| | | public int getRootLayoutId() {
|
| | | return R.layout.activity_capture;
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void onCreate(Bundle savedInstanceState) {
|
| | | super.onCreate(savedInstanceState);
|
| | | Window window = getWindow();
|
| | | window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
| | | //设置根视图
|
| | | View mContentView = LayoutInflater.from(this).inflate(getRootLayoutId(), null);
|
| | | setContentView(mContentView);
|
| | | afterViewBind(mContentView, savedInstanceState);
|
| | |
|
| | |
|
| | | }
|
| | |
|
| | | public void afterViewBind(View rootView, Bundle savedInstanceState) {
|
| | |
|
| | | light_iv = findViewById(R.id.light_iv);
|
| | | backll = findViewById(R.id.top_back_btn);
|
| | | backll.setOnClickListener(new View.OnClickListener() {
|
| | | @Override
|
| | | public void onClick(View v) {
|
| | | finish();
|
| | | }
|
| | | });
|
| | | scanPreview = findViewById(R.id.capture_preview);
|
| | | scanContainer = findViewById(R.id.capture_container);
|
| | | scanCropView = findViewById(R.id.capture_crop_view);
|
| | | ImageView scanLine = findViewById(R.id.capture_scan_line);
|
| | | inactivityTimer = new InactivityTimer(this);
|
| | | beepManager = new BeepManager(this);
|
| | |
|
| | | TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation
|
| | | .RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT,
|
| | | 0.9f);
|
| | | animation.setDuration(2000);
|
| | | animation.setRepeatCount(-1);
|
| | | animation.setRepeatMode(Animation.RESTART);
|
| | | scanLine.startAnimation(animation);
|
| | | light_iv.setOnClickListener(new View.OnClickListener() {
|
| | | @Override
|
| | | public void onClick(View v) {
|
| | | v.setSelected(!v.isSelected());
|
| | | if (v.isSelected()) {
|
| | | // cameraManager.open();
|
| | | light_iv.setImageDrawable(AppCompatResources.getDrawable(CaptureActivity.this, R.drawable.fast_scan_light_open));
|
| | | } else {
|
| | | // cameraManager.close();
|
| | | light_iv.setImageDrawable(AppCompatResources.getDrawable(CaptureActivity.this, R.drawable.fast_scan_light_close));
|
| | | }
|
| | |
|
| | | }
|
| | | });
|
| | |
|
| | | }
|
| | |
|
| | | public void enterGallery() {
|
| | | // 进入图库
|
| | | Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
| | | intent.addCategory(Intent.CATEGORY_OPENABLE);
|
| | | intent.setType("image/*");
|
| | | intent.putExtra("return-data", true);
|
| | | startActivityForResult(intent, REQUEST_CODE);
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void onResume() {
|
| | | super.onResume();
|
| | | cameraManager = new CameraManager(getApplication());
|
| | | handler = null;
|
| | | if (isHasSurface) {
|
| | | initCamera(scanPreview.getHolder());
|
| | | } else {
|
| | | scanPreview.getHolder().addCallback(this);
|
| | | }
|
| | |
|
| | | inactivityTimer.onResume();
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void onPause() {
|
| | | if (handler != null) {
|
| | | handler.quitSynchronously();
|
| | | handler = null;
|
| | | }
|
| | | inactivityTimer.onPause();
|
| | | beepManager.close();
|
| | | cameraManager.closeDriver();
|
| | | if (!isHasSurface) {
|
| | | scanPreview.getHolder().removeCallback(this);
|
| | | }
|
| | | super.onPause();
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void onDestroy() {
|
| | | inactivityTimer.shutdown();
|
| | | super.onDestroy();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void surfaceCreated(SurfaceHolder holder) {
|
| | | if (holder == null) {
|
| | | Log.d(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
|
| | | }
|
| | | if (!isHasSurface) {
|
| | | isHasSurface = true;
|
| | | initCamera(holder);
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void surfaceDestroyed(SurfaceHolder holder) {
|
| | | isHasSurface = false;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * A valid barcode has been found, so give an indication of success and show
|
| | | * the results.
|
| | | *
|
| | | * @param rawResult The contents of the barcode.
|
| | | * @param bundle The extras
|
| | | */
|
| | | @Override
|
| | | public void handleDecode(Result rawResult, Bundle bundle) {
|
| | | inactivityTimer.onActivity();
|
| | | beepManager.playBeepSoundAndVibrate();
|
| | | doProcess(rawResult.getText());
|
| | | restartPreviewAfterDelay(3000);
|
| | | }
|
| | |
|
| | | private void initCamera(SurfaceHolder surfaceHolder) {
|
| | | if (surfaceHolder == null) {
|
| | | throw new IllegalStateException("No SurfaceHolder provided");
|
| | | }
|
| | | if (cameraManager.isOpen()) {
|
| | | Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
|
| | | return;
|
| | | }
|
| | | try {
|
| | | cameraManager.openDriver(surfaceHolder);
|
| | | // Creating the handler starts the preview, which can also throw a
|
| | | // RuntimeException.
|
| | | if (handler == null) {
|
| | | handler = new CaptureActivityHandler(this, cameraManager, DecodeThread.ALL_MODE);
|
| | | }
|
| | |
|
| | | initCrop();
|
| | | } catch (IOException ioe) {
|
| | | Log.w(TAG, ioe);
|
| | | Toast.makeText(this, R.string.capture_no_camera, Toast.LENGTH_SHORT).show();
|
| | | finish();
|
| | | } catch (RuntimeException e) {
|
| | | Log.w(TAG, "Unexpected error initializing camera", e);
|
| | | Toast.makeText(this, R.string.capture_no_camera, Toast.LENGTH_SHORT).show();
|
| | | finish();
|
| | | }
|
| | | }
|
| | |
|
| | | public void restartPreviewAfterDelay(long delayMS) {
|
| | | if (handler != null) {
|
| | | handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public Rect getCropRect() {
|
| | | return mCropRect;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 初始化截取的矩形区域
|
| | | */
|
| | | private void initCrop() {
|
| | | int cameraWidth = cameraManager.getCameraResolution().y;
|
| | | int cameraHeight = cameraManager.getCameraResolution().x;
|
| | |
|
| | | /** 获取布局中扫描框的位置信息 */
|
| | | int[] location = new int[2];
|
| | | scanCropView.getLocationInWindow(location);
|
| | |
|
| | | int cropLeft = location[0];
|
| | | int cropTop = location[1] - getStatusBarHeight();
|
| | |
|
| | | int cropWidth = scanCropView.getWidth();
|
| | | int cropHeight = scanCropView.getHeight();
|
| | |
|
| | | /** 获取布局容器的宽高 */
|
| | | int containerWidth = scanContainer.getWidth();
|
| | | int containerHeight = scanContainer.getHeight();
|
| | |
|
| | | /** 计算最终截取的矩形的左上角顶点x坐标 */
|
| | | int x = cropLeft * cameraWidth / containerWidth;
|
| | | /** 计算最终截取的矩形的左上角顶点y坐标 */
|
| | | int y = cropTop * cameraHeight / containerHeight;
|
| | |
|
| | | /** 计算最终截取的矩形的宽度 */
|
| | | int width = cropWidth * cameraWidth / containerWidth;
|
| | | /** 计算最终截取的矩形的高度 */
|
| | | int height = cropHeight * cameraHeight / containerHeight;
|
| | |
|
| | | /** 生成最终的截取的矩形 */
|
| | | mCropRect = new Rect(x, y, width + x, height + y);
|
| | | }
|
| | |
|
| | | private int getStatusBarHeight() {
|
| | | try {
|
| | | Class<?> c = Class.forName("com.android.internal.R$dimen");
|
| | | Object obj = c.newInstance();
|
| | | Field field = c.getField("status_bar_height");
|
| | | int x = Integer.parseInt(field.get(obj).toString());
|
| | | return getResources().getDimensionPixelSize(x);
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return 0;
|
| | | }
|
| | |
|
| | | private void doProcess(String result) {
|
| | | Log.d("panlili", "scanResult: " + result);
|
| | | /*if (!DeviceHelper.getNetworkState()) {
|
| | | Toast.makeText(this, R.string.capture_no_network, Toast.LENGTH_SHORT).show();
|
| | | return;
|
| | | }*/
|
| | |
|
| | | if (TextUtils.isEmpty(result)) {
|
| | | Toast.makeText(this, R.string.capture_no_result, Toast.LENGTH_SHORT).show();
|
| | | } else {
|
| | | Intent intent = new Intent();
|
| | | intent.putExtra("data", result);
|
| | | setResult(RESULT_OK, intent);
|
| | | finish();
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
| | | super.onActivityResult(requestCode, resultCode, data);
|
| | | if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
|
| | | Uri originalUri = data.getData();
|
| | | if (originalUri != null) {
|
| | | String path = originalUri.getPath();
|
| | | String[] proj = {MediaStore.Images.Media.DATA};
|
| | | Cursor cursor = getContentResolver().query(originalUri, proj, null, null, null);
|
| | | if (cursor != null) {
|
| | | int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
| | | cursor.moveToFirst();
|
| | | path = cursor.getString(column_index);
|
| | | cursor.close();
|
| | | }
|
| | |
|
| | | if (!TextUtils.isEmpty(path)) {
|
| | | handleQRCodeFormPhoto(path);
|
| | | } else {
|
| | | Toast.makeText(this, "图片已损坏,请重新选择!", Toast.LENGTH_SHORT).show();
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 解析图库选择的二维码
|
| | | */
|
| | | public void handleQRCodeFormPhoto(final String filePath) {
|
| | | Thread dealThread = new Thread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | Hashtable<DecodeHintType, String> hints = new Hashtable<>();
|
| | | hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
|
| | | RGBLuminanceSource source = null;
|
| | | try {
|
| | | source = new RGBLuminanceSource(filePath);
|
| | | } catch (FileNotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
| | | QRCodeReader reader = new QRCodeReader();
|
| | | Result result;
|
| | | try {
|
| | | result = reader.decode(binaryBitmap, hints);
|
| | | if (!TextUtils.isEmpty(result.getText())) {
|
| | | dealUIInfo(result.getText());
|
| | | } else {
|
| | | dealUIInfo(null);
|
| | | }
|
| | | } catch (NotFoundException | ChecksumException | FormatException e) {
|
| | | dealUIInfo(null);
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | });
|
| | | dealThread.start();
|
| | | }
|
| | |
|
| | | private void dealUIInfo(final Object progressInfo) {
|
| | | runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (progressInfo == null) {
|
| | | Toast.makeText(CaptureActivity.this, R.string.capture_no_result2, Toast.LENGTH_SHORT).show();
|
| | | } else {
|
| | | doProcess(progressInfo.toString());
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | |
|
| | |
|
| | | } |
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.app.Activity;
|
| | | import android.content.Context;
|
| | | import android.content.SharedPreferences;
|
| | | import android.content.res.AssetFileDescriptor;
|
| | | import android.media.AudioManager;
|
| | | import android.media.MediaPlayer;
|
| | | import android.os.Vibrator;
|
| | | import android.preference.PreferenceManager;
|
| | | import android.util.Log;
|
| | |
|
| | | import com.zxing.R;
|
| | |
|
| | | import java.io.Closeable;
|
| | | import java.io.IOException;
|
| | |
|
| | | /**
|
| | | * Manages beeps and vibrations for {}.
|
| | | */
|
| | | public class BeepManager implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, Closeable {
|
| | |
|
| | | private static final String TAG = BeepManager.class.getSimpleName();
|
| | |
|
| | | private static final float BEEP_VOLUME = 0.10f;
|
| | | private static final long VIBRATE_DURATION = 200L;
|
| | |
|
| | | private final Activity activity;
|
| | | private MediaPlayer mediaPlayer;
|
| | | private boolean playBeep;
|
| | | private boolean vibrate;
|
| | |
|
| | | public BeepManager(Activity activity) {
|
| | | this.activity = activity;
|
| | | this.mediaPlayer = null;
|
| | | updatePrefs();
|
| | | }
|
| | |
|
| | | private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
|
| | | boolean shouldPlayBeep = true;
|
| | | if (shouldPlayBeep) {
|
| | | // See if sound settings overrides this
|
| | | AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
|
| | | if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
|
| | | shouldPlayBeep = false;
|
| | | }
|
| | | }
|
| | | return shouldPlayBeep;
|
| | | }
|
| | |
|
| | | private synchronized void updatePrefs() {
|
| | | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
| | | playBeep = shouldBeep(prefs, activity);
|
| | | vibrate = true;
|
| | | if (playBeep && mediaPlayer == null) {
|
| | | // The volume on STREAM_SYSTEM is not adjustable, and users found it
|
| | | // too loud,
|
| | | // so we now play on the music stream.
|
| | | activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
| | | mediaPlayer = buildMediaPlayer(activity);
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void playBeepSoundAndVibrate() {
|
| | | if (playBeep && mediaPlayer != null) {
|
| | | mediaPlayer.start();
|
| | | }
|
| | | if (vibrate) {
|
| | | Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
|
| | | vibrator.vibrate(VIBRATE_DURATION);
|
| | | }
|
| | | }
|
| | |
|
| | | private MediaPlayer buildMediaPlayer(Context activity) {
|
| | | MediaPlayer mediaPlayer = new MediaPlayer();
|
| | | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
| | | mediaPlayer.setOnCompletionListener(this);
|
| | | mediaPlayer.setOnErrorListener(this);
|
| | | try {
|
| | | AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep);
|
| | | try {
|
| | | mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
|
| | | } finally {
|
| | | file.close();
|
| | | }
|
| | | mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
|
| | | mediaPlayer.prepare();
|
| | | return mediaPlayer;
|
| | | } catch (IOException ioe) {
|
| | | Log.w(TAG, ioe);
|
| | | mediaPlayer.release();
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onCompletion(MediaPlayer mp) {
|
| | | // When the beep has finished playing, rewind to queue up another one.
|
| | | mp.seekTo(0);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
|
| | | if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
|
| | | // we are finished, so put up an appropriate error toast if required
|
| | | // and finish
|
| | | activity.finish();
|
| | | } else {
|
| | | // possibly media player error, so release and recreate
|
| | | mp.release();
|
| | | mediaPlayer = null;
|
| | | updatePrefs();
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public synchronized void close() {
|
| | | if (mediaPlayer != null) {
|
| | | mediaPlayer.release();
|
| | | mediaPlayer = null;
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.app.Activity;
|
| | | import android.content.Intent;
|
| | | import android.os.Bundle;
|
| | | import android.os.Handler;
|
| | | import android.os.Message;
|
| | |
|
| | | import com.google.zxing.Result;
|
| | | import com.zxing.IZxingActivity;
|
| | | import com.zxing.R;
|
| | | import com.zxing.camera.CameraManager;
|
| | | import com.zxing.decode.DecodeThread;
|
| | |
|
| | | /**
|
| | | * This class handles all the messaging which comprises the state machine for
|
| | | * capture.
|
| | | */
|
| | | public class CaptureActivityHandler extends Handler {
|
| | |
|
| | | private final IZxingActivity activity;
|
| | | private final DecodeThread decodeThread;
|
| | | private final CameraManager cameraManager;
|
| | | private State state;
|
| | |
|
| | | public CaptureActivityHandler(IZxingActivity activity, CameraManager cameraManager, int decodeMode) {
|
| | | this.activity = activity;
|
| | | decodeThread = new DecodeThread(activity, decodeMode);
|
| | | decodeThread.start();
|
| | | state = State.SUCCESS;
|
| | |
|
| | | // Start ourselves capturing previews and decoding.
|
| | | this.cameraManager = cameraManager;
|
| | | cameraManager.startPreview();
|
| | | restartPreviewAndDecode();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void handleMessage(Message message) {
|
| | | if (message.what == R.id.restart_preview) {
|
| | | restartPreviewAndDecode();
|
| | |
|
| | | } else if (message.what == R.id.decode_succeeded) {
|
| | | state = State.SUCCESS;
|
| | | Bundle bundle = message.getData();
|
| | |
|
| | | activity.handleDecode((Result) message.obj, bundle);
|
| | |
|
| | | } else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one
|
| | | // decode fails,
|
| | | // start another.
|
| | | state = State.PREVIEW;
|
| | | cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
| | |
|
| | | } else if (message.what == R.id.return_scan_result) {
|
| | | activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
|
| | | activity.finish();
|
| | |
|
| | | }
|
| | | }
|
| | |
|
| | | public void quitSynchronously() {
|
| | | state = State.DONE;
|
| | | try {
|
| | | cameraManager.stopPreview();
|
| | | } catch (RuntimeException e) {
|
| | |
|
| | | }
|
| | | Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
|
| | | quit.sendToTarget();
|
| | | try {
|
| | | // Wait at most half a second; should be enough time, and onPause()
|
| | | // will timeout quickly
|
| | | decodeThread.join(500L);
|
| | | } catch (InterruptedException e) {
|
| | | // continue
|
| | | }
|
| | |
|
| | | // Be absolutely sure we don't send any queued up messages
|
| | | removeMessages(R.id.decode_succeeded);
|
| | | removeMessages(R.id.decode_failed);
|
| | | }
|
| | |
|
| | | private void restartPreviewAndDecode() {
|
| | | if (state == State.SUCCESS) {
|
| | | state = State.PREVIEW;
|
| | | cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
| | | }
|
| | | }
|
| | |
|
| | | private enum State {
|
| | | PREVIEW, SUCCESS, DONE
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.annotation.SuppressLint;
|
| | | import android.app.Activity;
|
| | | import android.content.BroadcastReceiver;
|
| | | import android.content.Context;
|
| | | import android.content.Intent;
|
| | | import android.content.IntentFilter;
|
| | | import android.os.AsyncTask;
|
| | | import android.os.BatteryManager;
|
| | | import android.os.Build;
|
| | | import android.util.Log;
|
| | |
|
| | | /**
|
| | | * Finishes an activity after a period of inactivity if the device is on battery
|
| | | * power.
|
| | | */
|
| | | public class InactivityTimer {
|
| | |
|
| | | private static final String TAG = InactivityTimer.class.getSimpleName();
|
| | |
|
| | | private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
|
| | |
|
| | | private Activity activity;
|
| | | private BroadcastReceiver powerStatusReceiver;
|
| | | private boolean registered;
|
| | | private AsyncTask<Object, Object, Object> inactivityTask;
|
| | |
|
| | | public InactivityTimer(Activity activity) {
|
| | | this.activity = activity;
|
| | | powerStatusReceiver = new PowerStatusReceiver();
|
| | | registered = false;
|
| | | onActivity();
|
| | | }
|
| | |
|
| | | @SuppressLint("NewApi")
|
| | | public synchronized void onActivity() {
|
| | | cancel();
|
| | | inactivityTask = new InactivityAsyncTask();
|
| | | if (Build.VERSION.SDK_INT >= 11) {
|
| | | inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
| | | } else {
|
| | | inactivityTask.execute();
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void onPause() {
|
| | | cancel();
|
| | | if (registered) {
|
| | | activity.unregisterReceiver(powerStatusReceiver);
|
| | | registered = false;
|
| | | } else {
|
| | | Log.w(TAG, "PowerStatusReceiver was never registered?");
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void onResume() {
|
| | | if (registered) {
|
| | | Log.w(TAG, "PowerStatusReceiver was already registered?");
|
| | | } else {
|
| | | activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
| | | registered = true;
|
| | | }
|
| | | onActivity();
|
| | | }
|
| | |
|
| | | private synchronized void cancel() {
|
| | | AsyncTask<?, ?, ?> task = inactivityTask;
|
| | | if (task != null) {
|
| | | task.cancel(true);
|
| | | inactivityTask = null;
|
| | | }
|
| | | }
|
| | |
|
| | | public void shutdown() {
|
| | | cancel();
|
| | | }
|
| | |
|
| | | private class PowerStatusReceiver extends BroadcastReceiver {
|
| | | @Override
|
| | | public void onReceive(Context context, Intent intent) {
|
| | | if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
|
| | | // 0 indicates that we're on battery
|
| | | boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
|
| | | if (onBatteryNow) {
|
| | | InactivityTimer.this.onActivity();
|
| | | } else {
|
| | | InactivityTimer.this.cancel();
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | private class InactivityAsyncTask extends AsyncTask<Object, Object, Object> {
|
| | | @Override
|
| | | protected Object doInBackground(Object... objects) {
|
| | | try {
|
| | | Thread.sleep(INACTIVITY_DELAY_MS);
|
| | | Log.i(TAG, "Finishing activity due to inactivity");
|
| | | activity.finish();
|
| | | } catch (InterruptedException e) {
|
| | | // continue without killing
|
| | | }
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.text.TextUtils;
|
| | |
|
| | | import java.util.Iterator;
|
| | | import java.util.regex.Pattern;
|
| | |
|
| | | public abstract class Strings {
|
| | | public static final String EMPTY = "";
|
| | | public static final String BLANK = " ";
|
| | | public static final String EQUAL = "=";
|
| | | public static final String AND = "&";
|
| | | public static final String QMARK = "?";
|
| | | public static final String TRUE = "true";
|
| | | public static final String FALSE = "false";
|
| | | public static final String CANCEL = "cancel";
|
| | | public static final String NULL_STR = "null";
|
| | | public static final String SUCCESS = "success";
|
| | | public static final String FAIL = "fail";
|
| | | public static final String ZERO = "0";
|
| | | public static final String SEMICOLON = ";";
|
| | | public static final String SPLITE = "/";
|
| | | public static final String AT = "@";
|
| | | public static final String COMMA = ",";
|
| | | public static final String FILE_PRE = "file://";
|
| | | public static final String HTTP_PRE = "http";
|
| | | public static final String COLON = ":";
|
| | | public static final String WRAP = "";
|
| | | public static final String STAR = "*";
|
| | | public static final String MORE = "...";
|
| | |
|
| | | /**
|
| | | * 去除字符串中的空格
|
| | | */
|
| | | public static String trimAll(CharSequence s) {
|
| | | if (s == null || s.length() == 0) return Strings.EMPTY;
|
| | | StringBuilder sb = new StringBuilder(s.length());
|
| | | for (int i = 0, L = s.length(); i < L; i++) {
|
| | | char c = s.charAt(i);
|
| | | if (c != ' ') sb.append(c);
|
| | | }
|
| | | return sb.toString();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断是否为手机号码
|
| | | */
|
| | | public static boolean isPhoneNum(String phoneNum) {
|
| | | if (phoneNum == null || phoneNum.length() != 11) return false;
|
| | | if (!TextUtils.isDigitsOnly(phoneNum)) return false;
|
| | | Pattern p = Pattern.compile("^((13[0-9])|(14[0-9])|(15[^4,\\D])|(17[0-9])|(18[0-9]))\\d{8}$");
|
| | | return p.matcher(phoneNum).find();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断是否为座机号码
|
| | | */
|
| | | public static boolean isTelNum(String num) {
|
| | | if (TextUtils.isEmpty(num)) return false;
|
| | | Pattern p = Pattern.compile("(\\(\\d{3,4}\\)|\\d{3,4}-|\\s)?\\d{8}");
|
| | | return p.matcher(num).find();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断字符串中是否有中文,有中文返回true
|
| | | */
|
| | | public static boolean isChinese(String string) {
|
| | | boolean flag = false;
|
| | | for (int i = 0, L = string.length(); i < L; i++) {
|
| | | char c = string.charAt(i);
|
| | | if ((c >= 0x4e00) && (c <= 0x9FA5)) {
|
| | | flag = true;
|
| | | } else {
|
| | | return false;
|
| | | }
|
| | | }
|
| | | return flag;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断字符长度是否在范围里面。 当start end 为 -1 时,表示字符长度不考虑上线或下线
|
| | | *
|
| | | * @param str
|
| | | * @param start
|
| | | * @param end
|
| | | * @return
|
| | | */
|
| | | public static boolean isLengthInRange(String str, int start, int end) {
|
| | | boolean isInRange = true;
|
| | | int length = str.length();
|
| | | if (start != -1 && length < start) {
|
| | | isInRange = false;
|
| | | }
|
| | | if (end != -1 && end < length) {
|
| | | isInRange = false;
|
| | | }
|
| | | return isInRange;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 字符串转换为long型
|
| | | */
|
| | | public static long toLong(String text, long defaultVal) {
|
| | | if (TextUtils.isDigitsOnly(text)) {
|
| | | try {
|
| | | return Long.parseLong(text);
|
| | | } catch (NumberFormatException e) {
|
| | | }
|
| | | }
|
| | | return defaultVal;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 字符串转换为int
|
| | | */
|
| | | public static int toInt(String text, int defaultVal) {
|
| | | if (TextUtils.isDigitsOnly(text)) {
|
| | | try {
|
| | | return Integer.parseInt(text);
|
| | | } catch (NumberFormatException e) {
|
| | | }
|
| | | }
|
| | | return defaultVal;
|
| | | }
|
| | |
|
| | |
|
| | | public static String join(Iterable<?> iterable, String separator) {
|
| | |
|
| | | // handle null, zero and one elements before building a buffer
|
| | | if (iterable == null) {
|
| | | return null;
|
| | | }
|
| | | Iterator<?> iterator = iterable.iterator();
|
| | | if (!iterator.hasNext()) {
|
| | | return EMPTY;
|
| | | }
|
| | | Object first = iterator.next();
|
| | | if (!iterator.hasNext()) {
|
| | | return toString(first);
|
| | | }
|
| | |
|
| | | // two or more elements
|
| | | StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
|
| | | if (first != null) {
|
| | | buf.append(first);
|
| | | }
|
| | |
|
| | | while (iterator.hasNext()) {
|
| | | if (separator != null) {
|
| | | buf.append(separator);
|
| | | }
|
| | | Object obj = iterator.next();
|
| | | if (obj != null) {
|
| | | buf.append(obj);
|
| | | }
|
| | | }
|
| | | return buf.toString();
|
| | | }
|
| | |
|
| | | public static String toString(Object obj) {
|
| | | return obj == null ? "" : obj.toString();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 判断是否为字符串是否为空
|
| | | */
|
| | | public static boolean toBoolean(String property, boolean defaultVal) {
|
| | | return property == null ? defaultVal : Boolean.valueOf(property);
|
| | | }
|
| | |
|
| | | public static String format(String str, Object... obj) {
|
| | | try {
|
| | | return String.format(str, obj);
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return str;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.annotation.SuppressLint;
|
| | | import android.text.TextUtils;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | | import java.util.regex.Matcher;
|
| | | import java.util.regex.Pattern;
|
| | |
|
| | | /**
|
| | | * 正则表达式
|
| | | *
|
| | | */
|
| | | @SuppressLint("WrongConstant")
|
| | | public class Validator {
|
| | |
|
| | | public static boolean password(String password) {
|
| | | return password.matches("[0-9a-zA-Z]{8,16}") && !password.matches("[0-9]+") && !password.matches("[a-zA-Z]+");
|
| | | }
|
| | |
|
| | | public static List<String> findMac(String src) {
|
| | | List<String> list = new ArrayList<String>();
|
| | | if (src == null || "".equals(src))
|
| | | return list;
|
| | | Pattern pattern = Pattern
|
| | | .compile("[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}");
|
| | | Matcher matcher = pattern.matcher(src);
|
| | | while (matcher.find()) {
|
| | | list.add(matcher.group(0));
|
| | | }
|
| | | return list;
|
| | | }
|
| | |
|
| | | public static List<String> findColor(String src) {
|
| | | List<String> list = new ArrayList<String>();
|
| | | if (src == null || src.equals(""))
|
| | | return list;
|
| | | Pattern pattern = Pattern
|
| | | .compile("#[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8}");
|
| | | Matcher matcher = pattern.matcher(src);
|
| | | while (matcher.find()) {
|
| | | list.add(matcher.group(0));
|
| | | }
|
| | | return list;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 去除汉字,归正编码
|
| | | */
|
| | | public static String replaceHanzi(String input) {
|
| | | if (TextUtils.isEmpty(input)) {
|
| | | return "";
|
| | | }
|
| | |
|
| | | /**
|
| | | * 归正编码
|
| | | */
|
| | | byte[] bytes = input.getBytes();
|
| | | String info = "";
|
| | |
|
| | | for (int i = 0; i < bytes.length; i++) {
|
| | | if (bytes[i] < 0) {
|
| | | bytes[i] = 32;
|
| | | }
|
| | | info = info + new String(new byte[]{bytes[i]});
|
| | | }
|
| | |
|
| | | /**
|
| | | * 去除中文
|
| | | */
|
| | | Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
|
| | | Matcher m = p.matcher(info);
|
| | | List<String> inputs = new ArrayList<>();
|
| | |
|
| | | if (m.find()) {
|
| | | for (int i = 0; i < info.length(); i++) {
|
| | | String ever = info.substring(i, i + 1);
|
| | | Matcher m1 = p.matcher(ever);
|
| | | if (m1.find()) {
|
| | | ever = "";
|
| | | }
|
| | | inputs.add(ever);
|
| | | }
|
| | |
|
| | | String inputNew = "";
|
| | | for (int i = 0; i < inputs.size(); i++) {
|
| | | inputNew = inputNew + inputs.get(i);
|
| | | }
|
| | | return inputNew.trim();
|
| | |
|
| | | }
|
| | | return info.trim();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 验证邮箱
|
| | | */
|
| | | public static boolean checkEmail(String email) {
|
| | | boolean flag;
|
| | | try {
|
| | | String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
|
| | | Pattern regex = Pattern.compile(check);
|
| | | Matcher matcher = regex.matcher(email);
|
| | | flag = matcher.matches();
|
| | | } catch (Exception e) {
|
| | | flag = false;
|
| | | }
|
| | | return flag;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获得汉语拼音首字母
|
| | | */
|
| | | public static String getAlpha(String str) {
|
| | | if (str == null) {
|
| | | return "#";
|
| | | }
|
| | |
|
| | | if (str.trim().length() == 0) {
|
| | | return "#";
|
| | | }
|
| | |
|
| | | char c = str.trim().substring(0, 1).charAt(0);
|
| | | // 正则表达式,判断首字母是否是英文字母
|
| | | Pattern pattern = Pattern.compile("^[A-Za-z]+$");
|
| | | if (pattern.matcher(c + "").matches()) {
|
| | | return (c + "").toUpperCase();
|
| | | } else {
|
| | | return "#";
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 校验URL
|
| | | */
|
| | | public static boolean checkUrl(String url) {
|
| | | if (TextUtils.isEmpty(url)) return false;
|
| | | boolean flag;
|
| | | try {
|
| | | String check = "^((https|http|ftp|rtsp|mms|axd):\\/\\/)[^\\s]+";
|
| | | Pattern regex = Pattern.compile(check, Pattern.CASE_INSENSITIVE);
|
| | | Matcher matcher = regex.matcher(url.replaceAll(" ", ""));
|
| | | flag = matcher.matches();
|
| | | } catch (Exception e) {
|
| | | flag = false;
|
| | | }
|
| | |
|
| | | return flag;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 校验图片URL
|
| | | */
|
| | | public static boolean checkImageUrl(String url) {
|
| | | if (TextUtils.isEmpty(url)) return false;
|
| | | boolean flag;
|
| | | try {
|
| | | String check = "^((https|http):\\/\\/)[^\\s]+.(png|jpg|gif|webp)";
|
| | | Pattern regex = Pattern.compile(check, Pattern.CASE_INSENSITIVE);
|
| | | Matcher matcher = regex.matcher(url);
|
| | | flag = matcher.matches();
|
| | | } catch (Exception e) {
|
| | | flag = false;
|
| | | }
|
| | |
|
| | | return flag;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 校验正整数
|
| | | */
|
| | | public static boolean checkInt(String intContent) {
|
| | | if (TextUtils.isEmpty(intContent)) return false;
|
| | | boolean flag;
|
| | | try {
|
| | | String check = "^[1-9]\\d*$";
|
| | | Pattern regex = Pattern.compile(check, Pattern.CASE_INSENSITIVE);
|
| | | Matcher matcher = regex.matcher(intContent);
|
| | | flag = matcher.matches();
|
| | | } catch (Exception e) {
|
| | | flag = false;
|
| | | }
|
| | |
|
| | | return flag;
|
| | | }
|
| | |
|
| | | public static boolean checkColor(String color) {
|
| | | if (TextUtils.isEmpty(color))
|
| | | return false;
|
| | | boolean flag;
|
| | | try {
|
| | | String check = "^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$";
|
| | | Pattern regex = Pattern.compile(check, Pattern.CASE_INSENSITIVE);
|
| | | Matcher matcher = regex.matcher(color);
|
| | | flag = matcher.matches();
|
| | | } catch (Exception e) {
|
| | | flag = false;
|
| | | }
|
| | | return flag;
|
| | | }
|
| | | } |
New file |
| | |
| | | package com.zxing.utils;
|
| | |
|
| | | import android.content.ContentResolver;
|
| | | import android.content.Context;
|
| | | import android.content.Intent;
|
| | | import android.content.res.Resources;
|
| | | import android.graphics.Bitmap;
|
| | | import android.graphics.BitmapFactory;
|
| | | import android.graphics.Canvas;
|
| | | import android.graphics.Matrix;
|
| | | import android.graphics.PixelFormat;
|
| | | import android.graphics.drawable.Drawable;
|
| | | import android.media.ExifInterface;
|
| | | import android.net.Uri;
|
| | | import android.provider.MediaStore;
|
| | | import android.text.TextUtils;
|
| | | import android.util.Log;
|
| | |
|
| | | import java.io.BufferedOutputStream;
|
| | | import java.io.File;
|
| | | import java.io.FileDescriptor;
|
| | | import java.io.FileNotFoundException;
|
| | | import java.io.FileOutputStream;
|
| | | import java.io.IOException;
|
| | | import java.text.SimpleDateFormat;
|
| | | import java.util.Date;
|
| | |
|
| | | /**
|
| | | * Bitmap操作常用工具类
|
| | | */
|
| | | public class ZXingBitmapUtils {
|
| | |
|
| | | private final static String TAG = ZXingBitmapUtils.class.getCanonicalName();
|
| | | public final static String JPG_SUFFIX = ".jpg";
|
| | | private final static String TIME_FORMAT = "yyyyMMddHHmmss";
|
| | |
|
| | | /**
|
| | | * 显示图片到相册
|
| | | *
|
| | | * @param context
|
| | | * @param photoFile 要保存的图片文件
|
| | | */
|
| | | public static void displayToGallery(Context context, File photoFile) {
|
| | | if (photoFile == null || !photoFile.exists()) {
|
| | | return;
|
| | | }
|
| | | String photoPath = photoFile.getAbsolutePath();
|
| | | String photoName = photoFile.getName();
|
| | | // 其次把文件插入到系统图库
|
| | | try {
|
| | | ContentResolver contentResolver = context.getContentResolver();
|
| | | MediaStore.Images.Media.insertImage(contentResolver, photoPath, photoName, null);
|
| | | } catch (FileNotFoundException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | // 最后通知图库更新
|
| | | context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + photoPath)));
|
| | | }
|
| | |
|
| | | /**
|
| | | * 将Bitmap保存到指定目录下
|
| | | *
|
| | | * @param bitmap
|
| | | * @param folder
|
| | | * @return 保存成功,返回其对应的File,保存失败则返回null
|
| | | */
|
| | | public static File saveToFile(Bitmap bitmap, File folder) {
|
| | | String fileName = new SimpleDateFormat(TIME_FORMAT).format(new Date());//直接以当前时间戳作为文件名
|
| | | return saveToFile(bitmap, folder, fileName);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 将Bitmap保存到指定目录下,并且指定好文件名
|
| | | *
|
| | | * @param bitmap
|
| | | * @param folder
|
| | | * @param fileName 指定的文件名包含后缀
|
| | | * @return 保存成功,返回其对应的File,保存失败则返回null
|
| | | */
|
| | | public static File saveToFile(Bitmap bitmap, File folder, String fileName) {
|
| | | if (bitmap != null) {
|
| | | if (!folder.exists()) {
|
| | | folder.mkdir();
|
| | | }
|
| | | File file = new File(folder, fileName + JPG_SUFFIX);
|
| | | if (file.exists()) {
|
| | | file.delete();
|
| | | }
|
| | | try {
|
| | | file.createNewFile();
|
| | | BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
|
| | | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
|
| | | bos.flush();
|
| | | bos.close();
|
| | | return file;
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | return null;
|
| | | }
|
| | | } else {
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 获取图片的旋转角度
|
| | | *
|
| | | * @param path 图片绝对路径
|
| | | * @return 图片的旋转角度
|
| | | */
|
| | | public static int getBitmapDegree(String path) {
|
| | | int degree = 0;
|
| | | try {
|
| | | // 从指定路径下读取图片,并获取其EXIF信息
|
| | | ExifInterface exifInterface = new ExifInterface(path);
|
| | | // 获取图片的旋转信息
|
| | | int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
| | | switch (orientation) {
|
| | | case ExifInterface.ORIENTATION_ROTATE_90:
|
| | | degree = 90;
|
| | | break;
|
| | | case ExifInterface.ORIENTATION_ROTATE_180:
|
| | | degree = 180;
|
| | | break;
|
| | | case ExifInterface.ORIENTATION_ROTATE_270:
|
| | | degree = 270;
|
| | | break;
|
| | | }
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return degree;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 将图片按照指定的角度进行旋转
|
| | | *
|
| | | * @param bitmap 需要旋转的图片
|
| | | * @param degree 指定的旋转角度
|
| | | * @return 旋转后的图片
|
| | | */
|
| | | public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) {
|
| | | // 根据旋转角度,生成旋转矩阵
|
| | | Matrix matrix = new Matrix();
|
| | | matrix.postRotate(degree);
|
| | | // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
|
| | | Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
| | | if (bitmap != null && !bitmap.isRecycled()) {
|
| | | bitmap.recycle();
|
| | | }
|
| | | return newBitmap;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 压缩Bitmap的大小
|
| | | *
|
| | | * @param imageFile 图片文件
|
| | | * @param requestWidth 压缩到想要的宽度
|
| | | * @param requestHeight 压缩到想要的高度
|
| | | * @return
|
| | | */
|
| | | public static Bitmap decodeBitmapFromFile(File imageFile, int requestWidth, int requestHeight) {
|
| | | if (imageFile != null) {
|
| | | return decodeBitmapFromFile(imageFile.getAbsolutePath(), requestWidth, requestHeight);
|
| | | } else {
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 压缩Bitmap的大小
|
| | | *
|
| | | * @param imagePath 图片文件路径
|
| | | * @param requestWidth 压缩到想要的宽度
|
| | | * @param requestHeight 压缩到想要的高度
|
| | | * @return
|
| | | */
|
| | | public static Bitmap decodeBitmapFromFile(String imagePath, int requestWidth, int requestHeight) {
|
| | | if (!TextUtils.isEmpty(imagePath)) {
|
| | | Log.i(TAG, "requestWidth: " + requestWidth);
|
| | | Log.i(TAG, "requestHeight: " + requestHeight);
|
| | | if (requestWidth <= 0 || requestHeight <= 0) {
|
| | | Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
|
| | | return bitmap;
|
| | | }
|
| | | BitmapFactory.Options options = new BitmapFactory.Options();
|
| | | options.inJustDecodeBounds = true;//不加载图片到内存,仅获得图片宽高
|
| | | BitmapFactory.decodeFile(imagePath, options);
|
| | | Log.i(TAG, "original height: " + options.outHeight);
|
| | | Log.i(TAG, "original width: " + options.outWidth);
|
| | | if (options.outHeight == -1 || options.outWidth == -1) {
|
| | | try {
|
| | | ExifInterface exifInterface = new ExifInterface(imagePath);
|
| | | int height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, ExifInterface.ORIENTATION_NORMAL);//获取图片的高度
|
| | | int width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, ExifInterface.ORIENTATION_NORMAL);//获取图片的宽度
|
| | | Log.i(TAG, "exif height: " + height);
|
| | | Log.i(TAG, "exif width: " + width);
|
| | | options.outWidth = width;
|
| | | options.outHeight = height;
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | options.inSampleSize = calculateInSampleSize(options, requestWidth, requestHeight); //计算获取新的采样率
|
| | | Log.i(TAG, "inSampleSize: " + options.inSampleSize);
|
| | | options.inJustDecodeBounds = false;
|
| | | return BitmapFactory.decodeFile(imagePath, options);
|
| | |
|
| | | } else {
|
| | | return null;
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * Decode and sample down a bitmap from resources to the requested width and height.
|
| | | *
|
| | | * @param res The resources object containing the image data
|
| | | * @param resId The resource id of the image data
|
| | | * @param reqWidth The requested width of the resulting bitmap
|
| | | * @param reqHeight The requested height of the resulting bitmap
|
| | | * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
| | | * that are equal to or greater than the requested width and height
|
| | | */
|
| | | public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
|
| | |
|
| | | // BEGIN_INCLUDE (read_bitmap_dimensions)
|
| | | // First decode with inJustDecodeBounds=true to check dimensions
|
| | | final BitmapFactory.Options options = new BitmapFactory.Options();
|
| | | options.inJustDecodeBounds = true;
|
| | | BitmapFactory.decodeResource(res, resId, options);
|
| | |
|
| | | // Calculate inSampleSize
|
| | | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
| | | // END_INCLUDE (read_bitmap_dimensions)
|
| | |
|
| | | // Decode bitmap with inSampleSize set
|
| | | options.inJustDecodeBounds = false;
|
| | | return BitmapFactory.decodeResource(res, resId, options);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Decode and sample down a bitmap from a file input stream to the requested width and height.
|
| | | *
|
| | | * @param fileDescriptor The file descriptor to read from
|
| | | * @param reqWidth The requested width of the resulting bitmap
|
| | | * @param reqHeight The requested height of the resulting bitmap
|
| | | * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
|
| | | * that are equal to or greater than the requested width and height
|
| | | */
|
| | | public static Bitmap decodeBitmapFromDescriptor(FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {
|
| | |
|
| | | // First decode with inJustDecodeBounds=true to check dimensions
|
| | | final BitmapFactory.Options options = new BitmapFactory.Options();
|
| | | options.inJustDecodeBounds = true;
|
| | | BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
| | |
|
| | | // Calculate inSampleSize
|
| | | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
| | |
|
| | | // Decode bitmap with inSampleSize set
|
| | | options.inJustDecodeBounds = false;
|
| | |
|
| | | return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Google官方代码,计算合适的采样率
|
| | | * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
|
| | | * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
|
| | | * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
|
| | | * having a width and height equal to or larger than the requested width and height.
|
| | | *
|
| | | * @param options An options object with out* params already populated (run through a decode*
|
| | | * method with inJustDecodeBounds==true
|
| | | * @param reqWidth The requested width of the resulting bitmap
|
| | | * @param reqHeight The requested height of the resulting bitmap
|
| | | * @return The value to be used for inSampleSize
|
| | | */
|
| | | public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
|
| | | // BEGIN_INCLUDE (calculate_sample_size)
|
| | | // Raw height and width of image
|
| | | final int height = options.outHeight;
|
| | | final int width = options.outWidth;
|
| | | int inSampleSize = 1;
|
| | |
|
| | | if (height > reqHeight || width > reqWidth) {
|
| | |
|
| | | final int halfHeight = height / 2;
|
| | | final int halfWidth = width / 2;
|
| | |
|
| | | // Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
| | | // height and width larger than the requested height and width.
|
| | | while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
|
| | | inSampleSize *= 2;
|
| | | }
|
| | |
|
| | | // This offers some additional logic in case the image has a strange
|
| | | // aspect ratio. For example, a panorama may have a much larger
|
| | | // width than height. In these cases the total pixels might still
|
| | | // end up being too large to fit comfortably in memory, so we should
|
| | | // be more aggressive with sample down the image (=larger inSampleSize).
|
| | |
|
| | | long totalPixels = width * height / inSampleSize;
|
| | |
|
| | | // Anything more than 2x the requested pixels we'll sample down further
|
| | | final long totalReqPixelsCap = reqWidth * reqHeight * 2;
|
| | |
|
| | | while (totalPixels > totalReqPixelsCap) {
|
| | | inSampleSize *= 2;
|
| | | totalPixels /= 2;
|
| | | }
|
| | | }
|
| | | return inSampleSize;
|
| | | // END_INCLUDE (calculate_sample_size)
|
| | | }
|
| | |
|
| | | /**
|
| | | * drawable转bitmap
|
| | | */
|
| | | public static Bitmap drawableToBitmap(Drawable drawable) {
|
| | | int w = drawable.getIntrinsicWidth();
|
| | | int h = drawable.getIntrinsicHeight();
|
| | | Bitmap.Config config =
|
| | | drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
|
| | | : Bitmap.Config.RGB_565;
|
| | | Bitmap bitmap = Bitmap.createBitmap(w, h, config);
|
| | | //注意,下面三行代码要用到,否在在View或者surfaceview里的canvas.drawBitmap会看不到图
|
| | | Canvas canvas = new Canvas(bitmap);
|
| | | drawable.setBounds(0, 0, w, h);
|
| | | drawable.draw(canvas);
|
| | | return bitmap;
|
| | | }
|
| | | }
|
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android">
|
| | | <size android:height="13dp"/>
|
| | | <solid android:color="@color/transparent"/>
|
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| | | android:id="@+id/capture_container"
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="match_parent"
|
| | | android:background="#636363">
|
| | |
|
| | |
|
| | |
|
| | | <RelativeLayout
|
| | | android:id="@+id/top_bar_view"
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="52dp"
|
| | | android:background="#245EC3"
|
| | | android:orientation="horizontal">
|
| | |
|
| | | <!--1.返回按钮 增大点击区域-->
|
| | | <LinearLayout
|
| | | android:id="@+id/top_back_btn"
|
| | | android:layout_width="56dp"
|
| | | android:layout_height="match_parent"
|
| | | android:gravity="center_vertical"
|
| | | android:orientation="horizontal"
|
| | | >
|
| | |
|
| | | <ImageView
|
| | | android:layout_width="24dp"
|
| | | android:layout_height="24dp"
|
| | | android:layout_gravity="center"
|
| | | android:layout_marginLeft="16dp"
|
| | | android:scaleType="centerInside"
|
| | | android:src="@drawable/back"
|
| | | />
|
| | | </LinearLayout>
|
| | |
|
| | | <!--2.标题文本-->
|
| | | <TextView
|
| | | android:id="@+id/top_title_tv"
|
| | | android:layout_width="wrap_content"
|
| | | android:layout_height="wrap_content"
|
| | | android:layout_centerInParent="true"
|
| | | android:layout_marginLeft="60dp"
|
| | | android:layout_marginRight="60dp"
|
| | | android:fontFamily="sans-serif-medium"
|
| | | android:gravity="center"
|
| | | android:maxLines="1"
|
| | | android:text="快速扫码"
|
| | | android:textColor="#FFFFFFFF"
|
| | | android:textSize="18sp" />
|
| | |
|
| | | <!--3.更多按钮 默认隐藏-->
|
| | | <LinearLayout
|
| | | android:id="@+id/top_more_btn"
|
| | | android:layout_width="wrap_content"
|
| | | android:layout_height="match_parent"
|
| | | android:layout_alignParentEnd="true"
|
| | | android:gravity="center_vertical"
|
| | | android:orientation="horizontal">
|
| | |
|
| | | <ImageView
|
| | | android:id="@+id/top_more_iv"
|
| | | android:layout_width="28dp"
|
| | | android:layout_height="28dp"
|
| | | android:layout_marginStart="20dp"
|
| | | android:layout_marginEnd="20dp"
|
| | | android:adjustViewBounds="true"
|
| | | android:scaleType="centerInside"
|
| | | android:visibility="gone" />
|
| | |
|
| | | </LinearLayout>
|
| | |
|
| | | </RelativeLayout>
|
| | |
|
| | | <SurfaceView
|
| | | android:id="@+id/capture_preview"
|
| | | android:layout_width="367dp"
|
| | | android:layout_height="367dp"
|
| | | android:layout_centerInParent="true"
|
| | |
|
| | | />
|
| | |
|
| | |
|
| | | <RelativeLayout
|
| | | android:id="@+id/capture_crop_view"
|
| | | android:layout_width="367dp"
|
| | | android:layout_height="367dp"
|
| | | android:layout_centerInParent="true"
|
| | | android:background="@drawable/scan_capture">
|
| | |
|
| | | <RelativeLayout
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="367dp"
|
| | | android:layout_marginTop="182dp">
|
| | |
|
| | | <ImageView
|
| | | android:id="@+id/capture_scan_line"
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="wrap_content"
|
| | | android:layout_alignParentTop="true"
|
| | | android:layout_marginTop="5dp"
|
| | | android:layout_marginBottom="5dp"
|
| | | android:src="@drawable/scan_line" />
|
| | | </RelativeLayout>
|
| | |
|
| | | </RelativeLayout>
|
| | |
|
| | | <ImageView
|
| | | android:id="@+id/light_iv"
|
| | | android:layout_width="127dp"
|
| | | android:layout_height="127dp"
|
| | | android:layout_alignParentBottom="true"
|
| | | android:layout_centerInParent="true"
|
| | | android:layout_marginBottom="13dp"
|
| | | android:background="@drawable/fast_scan_light_close" />
|
| | |
|
| | |
|
| | | </RelativeLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <RelativeLayout
|
| | | xmlns:android="http://schemas.android.com/apk/res/android"
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="50dp"
|
| | | >
|
| | |
|
| | | <RelativeLayout
|
| | | android:id="@+id/rl_back"
|
| | | android:layout_width="wrap_content"
|
| | | android:layout_height="match_parent"
|
| | | android:layout_marginStart="32dp">
|
| | |
|
| | | <TextView
|
| | | android:id="@+id/tv_text_cancel"
|
| | | android:layout_width="wrap_content"
|
| | | android:layout_height="wrap_content"
|
| | | android:layout_centerVertical="true"
|
| | | android:text="@string/scan_cancel"
|
| | | android:textColor="#000000"
|
| | | android:textSize="36sp" />
|
| | | </RelativeLayout>
|
| | |
|
| | | <TextView
|
| | | android:id="@+id/tv_text_scan"
|
| | | android:layout_width="wrap_content"
|
| | | android:layout_height="wrap_content"
|
| | | android:layout_centerInParent="true"
|
| | | android:text="@string/scan_title"
|
| | | android:textColor="#000000"
|
| | | android:textSize="36sp" />
|
| | |
|
| | |
|
| | | </RelativeLayout>
|
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <resources>
|
| | | <!--基础色-->
|
| | | <color name="c0">#F18D00</color> <!--用于按钮、较大面积色块辅色 原C10-->
|
| | | <color name="c1">#F18D00</color> <!--用于强调性文字/图片等非大面积色块色值-->
|
| | | <color name="c2">#2C2C2C</color> <!--主标题/主文字色-->
|
| | | <!--主标题/主文字色-->
|
| | | <color name="c3">#C8D1E6</color> <!--Toolbar文字/普通按钮BG色-->
|
| | | <!--点缀色-->
|
| | | <color name="c5">#888888</color> <!--普通文案/引导/次要文字-->
|
| | | <color name="c6">#CCCCCC</color> <!--置灰/输入提示-->
|
| | |
|
| | | <!--底色/分割-->
|
| | | <color name="c7">#F7F7F7</color> <!--客户端底色/卡片描边-->
|
| | | <color name="c8">#F2F2F2</color> <!--分割线底色-->
|
| | | <color name="c10">#FFFFFF</color> <!--反白-->
|
| | |
|
| | | <!--带透明度的颜色-->
|
| | | <!--全局遮罩-->
|
| | | <color name="c14">#7FF18D00</color> <!--强调钮pressed-->
|
| | | <color name="c15">#7FF18D00</color> <!--强调钮disabled-->
|
| | | <!--普通钮pressed-->
|
| | | <!--普通钮disabled-->
|
| | |
|
| | | <!--成功失败提示-->
|
| | | <color name="c12">#EF6545</color> <!--删除、警示色;添加设备超时红灯文案-->
|
| | | <color name="color_background">#ffffff</color>
|
| | | <color name="transparent">#00000000</color>
|
| | |
|
| | | <!--新UI规范颜色值统一命名-->
|
| | | <color name="color5">#424243</color>
|
| | | <color name="color9">#cccccc</color>
|
| | | </resources>
|
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <resources>
|
| | | <item name="decode" type="id"/>
|
| | | <item name="decode_failed" type="id"/>
|
| | | <item name="decode_succeeded" type="id"/>
|
| | | <item name="quit" type="id"/>
|
| | | <item name="restart_preview" type="id"/>
|
| | | <item name="return_scan_result" type="id"/>
|
| | | </resources> |
New file |
| | |
| | | <resources>
|
| | | <string name="app_name">third_zxing2</string>
|
| | | <string name="scan_title">二维码扫描</string>
|
| | |
|
| | | <!--扫码-->
|
| | | <string name="zxing_scan_tips">将二维码放入框内,即可自动扫描</string>
|
| | | <string name="capture_no_camera">没有访问相机的权限,请打开访问权限再试</string>
|
| | | <string name="capture_no_result">没有扫描出结果</string>
|
| | | <string name="capture_no_network">当前网络不可用,请检查网络后再试</string>
|
| | | <string name="capture_no_result2">没有扫描出结果,可能不是有效的二维码</string>
|
| | | <string name="scan_cancel">取消</string>
|
| | |
|
| | | </resources>
|
New file |
| | |
| | | <resources>
|
| | |
|
| | | <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
|
| | | <item name="android:windowBackground">@color/color_background</item>
|
| | | <item name="android:listViewStyle">@style/List</item>
|
| | | <item name="android:windowAnimationStyle">@null</item>
|
| | | <item name="android:textViewStyle">@style/TextView</item>
|
| | | <item name="android:disabledAlpha">1</item>
|
| | | <item name="android:listDivider">@drawable/transparent_divider</item>
|
| | | </style>
|
| | |
|
| | | <style name="List" parent="@android:style/Widget.ListView">
|
| | | <item name="android:listSelector">@color/transparent</item>
|
| | | <item name="android:cacheColorHint">@color/transparent</item>
|
| | | <item name="android:divider">@null</item>
|
| | | </style>
|
| | |
|
| | |
|
| | | <style name="TextView" parent="@android:style/Widget.TextView">
|
| | | <item name="android:textColorHint">@color/color9</item>
|
| | | <item name="android:textColor">@color/color5</item>
|
| | | <item name="android:textSize">14sp</item>
|
| | | </style>
|
| | | </resources>
|