android13修改WiFi掃描二維碼識別識別成功率不高的問題

Android13 Setting掃描二維碼主要用到了WifiDppQrCodeScannerFragment

WifiDppQrCodeScannerFragment 依賴? QrCamera 類。

QrCamera 使用了 Camera1 的API。

開發了新類? ModernQrScanner ,采用了Camera2和更新了最新的Zxing包。

添加一個新的二維碼掃描的處理類,放在com.android.settings.wifi.dpp 包下面。

package com.android.settings.wifi.dpp;import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;/*** 現代化QR碼掃描工具類,基于Camera2 API* 提供更好的兼容性、穩定性和識別率*/
public class ModernQrScanner {private static final String TAG = "ModernQrScanner";// 相機相關private CameraManager mCameraManager;private CameraDevice mCameraDevice;private CameraCaptureSession mCaptureSession;private ImageReader mImageReader;private Size mPreviewSize;private String mCameraId;// 線程管理private HandlerThread mBackgroundThread;private Handler mBackgroundHandler;private volatile Handler mMainHandler;// 同步控制private final Semaphore mCameraOpenCloseLock = new Semaphore(1);private final AtomicBoolean mIsScanning = new AtomicBoolean(false);private final AtomicBoolean mIsCameraOpen = new AtomicBoolean(false);// QR碼解碼private MultiFormatReader mMultiFormatReader;private static final Map<DecodeHintType, Object> DECODE_HINTS = new EnumMap<>(DecodeHintType.class);// 配置參數private static final int MAX_PREVIEW_WIDTH = 1920;private static final int MAX_PREVIEW_HEIGHT = 1080;private static final int MIN_PREVIEW_WIDTH = 640;private static final int MIN_PREVIEW_HEIGHT = 480;private static final long SCAN_INTERVAL_MS = 200;// 回調接口private volatile ScanCallback mScanCallback;private Context mContext;static {// 配置ZXing解碼參數DECODE_HINTS.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(BarcodeFormat.QR_CODE, BarcodeFormat.DATA_MATRIX, BarcodeFormat.AZTEC, BarcodeFormat.PDF_417));DECODE_HINTS.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);DECODE_HINTS.put(DecodeHintType.CHARACTER_SET, "UTF-8");DECODE_HINTS.put(DecodeHintType.ALSO_INVERTED, Boolean.TRUE);}public interface ScanCallback {/*** 掃描成功回調** @param result QR碼內容* @param format 條碼格式*/void onScanSuccess(String result, BarcodeFormat format);/*** 掃描失敗回調** @param error 錯誤信息*/void onScanError(String error);/*** 相機狀態回調** @param isOpen 相機是否已開啟*/void onCameraStateChanged(boolean isOpen);/*** 獲取掃描區域** @param previewSize 預覽尺寸* @return 掃描區域,null表示全屏掃描*/Rect getScanRect(Size previewSize);void testGetScan(Bitmap bitmap);}public ModernQrScanner(@NonNull Context context) {mContext = context.getApplicationContext();mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);mMainHandler = new Handler(Looper.getMainLooper());initializeReader();}private void initializeReader() {mMultiFormatReader = new MultiFormatReader();mMultiFormatReader.setHints(DECODE_HINTS);}/*** 開始掃描** @param surface  預覽Surface* @param callback 掃描回調*/public void startScanning(@NonNull Surface surface, @NonNull ScanCallback callback) {if (!checkCameraPermission()) {callback.onScanError("Camera permission not granted");return;}mScanCallback = callback;startBackgroundThread();try {if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {callback.onScanError("Time out waiting to lock camera opening.");return;}openCamera(surface);} catch (InterruptedException e) {callback.onScanError("Interrupted while trying to lock camera opening: " + e.getMessage());}}public void startScanning(TextureView textureView, ScanCallback callback) {if (!checkCameraPermission() && textureView != null) {callback.onScanError("Camera permission not granted");return;}mScanCallback = callback;startBackgroundThread();try {if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {callback.onScanError("Time out waiting to lock camera opening.");return;}if (textureView.isAvailable()) {openCamera(new Surface(textureView.getSurfaceTexture()));} else {textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {openCamera(new Surface(surfaceTexture));}@Overridepublic void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {}@Overridepublic boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {closeCamera();return false;}@Overridepublic void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {}});}} catch (InterruptedException e) {callback.onScanError("Interrupted while trying to lock camera opening: " + e.getMessage());}}/*** 停止掃描*/public void stopScanning() {mIsScanning.set(false);closeCamera();stopBackgroundThread();
//        if (mScanCallback != null && mMainHandler != null) {
//            mMainHandler.post(() -> mScanCallback.onCameraStateChanged(false));
//        }}private boolean checkCameraPermission() {return ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;}private void startBackgroundThread() {mBackgroundThread = new HandlerThread("CameraBackground");mBackgroundThread.start();mBackgroundHandler = new Handler(mBackgroundThread.getLooper());}private void stopBackgroundThread() {if (mBackgroundThread != null) {mBackgroundThread.quitSafely();try {mBackgroundThread.join();mBackgroundThread = null;mBackgroundHandler = null;} catch (InterruptedException e) {Log.e(TAG, "Error stopping background thread", e);}}}@SuppressLint("MissingPermission")private void openCamera(@NonNull Surface surface) {try {
//            Log.e(TAG, " openCamera ");setupCameraParameters();setupImageReader();mCameraManager.openCamera(mCameraId, new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraOpenCloseLock.release();mCameraDevice = camera;mIsCameraOpen.set(true);createCameraPreviewSession(surface);if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onCameraStateChanged(true));}}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {mCameraOpenCloseLock.release();camera.close();mCameraDevice = null;mIsCameraOpen.set(false);if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onCameraStateChanged(false));}}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {mCameraOpenCloseLock.release();camera.close();mCameraDevice = null;mIsCameraOpen.set(false);if (mScanCallback != null) {String errorMsg = "Camera error: " + error;mMainHandler.post(() -> {mScanCallback.onScanError(errorMsg);mScanCallback.onCameraStateChanged(false);});}}}, mBackgroundHandler);} catch (Exception e) {Log.e(TAG, "Failed to open camera", e);if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onScanError("Failed to open camera: " + e.getMessage()));}}}private void setupCameraParameters() throws CameraAccessException {mCameraId = selectBestCamera();if (mCameraId == null) {throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);}CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId);StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);}// 選擇最佳預覽尺寸Size[] outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);mPreviewSize = chooseBestSize(outputSizes, MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT);Log.d(TAG, "Selected camera: " + mCameraId + ", preview size: " + mPreviewSize);}private String selectBestCamera() throws CameraAccessException {String[] cameraIds = mCameraManager.getCameraIdList();// 優先選擇后置攝像頭for (String cameraId : cameraIds) {CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {// 檢查是否支持自動對焦int[] afModes = characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);if (afModes != null && afModes.length > 0) {return cameraId;}}}// 如果沒有后置攝像頭,選擇第一個可用的return cameraIds.length > 0 ? cameraIds[0] : null;}private Size chooseBestSize(Size[] choices, int maxWidth, int maxHeight) {List<Size> bigEnough = new ArrayList<>();List<Size> notBigEnough = new ArrayList<>();for (Size option : choices) {if (option.getWidth() >= MIN_PREVIEW_WIDTH && option.getHeight() >= MIN_PREVIEW_HEIGHT) {if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight) {bigEnough.add(option);} else {notBigEnough.add(option);}}}// 選擇滿足條件的最大尺寸,或者最小的可用尺寸if (bigEnough.size() > 0) {return Collections.max(bigEnough, new CompareSizesByArea());} else if (notBigEnough.size() > 0) {return Collections.min(notBigEnough, new CompareSizesByArea());} else {Log.w(TAG, "Couldn't find any suitable preview size");return choices[0];}}private void setupImageReader() {//        mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 2);mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 3);mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {if (mIsScanning.get()) {processImage(reader.acquireLatestImage());}}}, mBackgroundHandler);
//        Log.e(TAG, " setupImageReader ===============  ok  ");}private void createCameraPreviewSession(@NonNull Surface surface) {try {List<Surface> surfaces = Arrays.asList(surface, mImageReader.getSurface());mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {if (mCameraDevice == null) {return;}mCaptureSession = cameraCaptureSession;startRepeatingRequest(surface);}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onScanError("Failed to configure camera"));}}}, mBackgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "Failed to create camera preview session", e);if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onScanError("Failed to create preview session: " + e.getMessage()));}}}private void startRepeatingRequest(Surface surface) {try {CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);requestBuilder.addTarget(mImageReader.getSurface());requestBuilder.addTarget(surface);// 設置自動對焦模式requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);// 設置自動曝光模式requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);// 設置自動白平衡requestBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);// 設置場景模式為條碼掃描(如果支持)requestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_BARCODE);mCaptureSession.setRepeatingRequest(requestBuilder.build(), new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {// 開始掃描mIsScanning.set(true);}}, mBackgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "Failed to start repeating request", e);if (mScanCallback != null) {mMainHandler.post(() -> mScanCallback.onScanError("Failed to start camera preview: " + e.getMessage()));}}}private volatile boolean inAction = false;private void processImage(Image image) {
//        Log.e(TAG, "processImage ... ");if (image == null || !mIsScanning.get()) {if (image != null) {image.close();}return;}try {// 轉換Image到byte數組Image.Plane[] planes = image.getPlanes();ByteBuffer byteBuffer = planes[0].getBuffer();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0,bitmap.getWidth(), bitmap.getHeight());RGBLuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), pixels);BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));if (mScanCallback != null && !inAction) {inAction = true;mScanCallback.testGetScan(bitmap);inAction = false;}Result result = mMultiFormatReader.decode(binaryBitmap);if (result != null) {mIsScanning.set(false); // 停止掃描if (mScanCallback != null) {final String text = result.getText();final BarcodeFormat format = result.getBarcodeFormat();mMainHandler.post(() -> mScanCallback.onScanSuccess(text, format));}}} catch (ReaderException e) {Log.e(TAG, "Error processing image", e);// 解碼失敗,繼續掃描e.printStackTrace();} catch (Exception e) {Log.e(TAG, "Error processing image", e);e.printStackTrace();} finally {mMultiFormatReader.reset();image.close();
//            inAction = false;// 添加掃描間隔,避免過度消耗CPUif (mIsScanning.get() && mBackgroundHandler != null) {mBackgroundHandler.postDelayed(() -> {// 延遲后繼續掃描}, SCAN_INTERVAL_MS);}}}private void closeCamera() {try {mCameraOpenCloseLock.acquire();if (mCaptureSession != null) {mCaptureSession.close();mCaptureSession = null;}if (mCameraDevice != null) {mCameraDevice.close();mCameraDevice = null;}if (mImageReader != null) {mImageReader.close();mImageReader = null;}mIsCameraOpen.set(false);} catch (InterruptedException e) {Log.e(TAG, "Interrupted while trying to lock camera closing", e);} finally {mCameraOpenCloseLock.release();}}/*** 檢查相機是否已開啟*/public boolean isCameraOpen() {return mIsCameraOpen.get();}/*** 檢查是否正在掃描*/public boolean isScanning() {return mIsScanning.get();}/*** 手動觸發對焦*/public void triggerFocus() {if (mCaptureSession != null && mCameraDevice != null) {try {CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);requestBuilder.addTarget(mImageReader.getSurface());requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);mCaptureSession.capture(requestBuilder.build(), null, mBackgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "Failed to trigger focus", e);}}}/*** 重新開始掃描*/public void resumeScanning() {mIsScanning.set(true);}/*** 暫停掃描*/public void pauseScanning() {mIsScanning.set(false);}/*** 尺寸比較器*/private static class CompareSizesByArea implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());}}/*** 資源清理*/public void release() {stopScanning();mScanCallback = null;mContext = null;}
}

WifiDppQrCodeScannerFragment 做了修改適配了新的掃描類 ModernQrScanner。

/** Copyright (C) 2018 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.settings.wifi.dpp;import static android.net.wifi.WifiInfo.sanitizeSsid;
import android.graphics.Bitmap;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.net.wifi.EasyConnectStatusCallback;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SimpleClock;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import android.content.pm.ActivityInfo;
import com.google.zxing.BarcodeFormat;
import androidx.annotation.StringRes;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.ViewModelProviders;import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.qrcode.QrCamera;
import com.android.settingslib.qrcode.QrDecorateView;
import com.android.settingslib.wifi.WifiPermissionChecker;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;import android.view.Surface;import java.time.Clock;
import java.time.ZoneOffset;
import java.util.List;public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment implementsSurfaceTextureListener,QrCamera.ScannerCallback,WifiManager.ActionListener {private static final String TAG = "WifiDppQrCodeScanner";/*** Message sent to hide error message*/private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;/*** Message sent to show error message*/private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;/*** Message sent to manipulate Wi-Fi DPP QR code*/private static final int MESSAGE_SCAN_WIFI_DPP_SUCCESS = 3;/*** Message sent to manipulate ZXing Wi-Fi QR code*/private static final int MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS = 4;private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;// Key for Bundle usageprivate static final String KEY_IS_CONFIGURATOR_MODE = "key_is_configurator_mode";private static final String KEY_LATEST_ERROR_CODE = "key_latest_error_code";public static final String KEY_WIFI_CONFIGURATION = "key_wifi_configuration";private static final int ARG_RESTART_CAMERA = 1;// Max age of tracked WifiEntries.private static final long MAX_SCAN_AGE_MILLIS = 15_000;// Interval between initiating WifiPickerTracker scans.private static final long SCAN_INTERVAL_MILLIS = 10_000;//    private QrCamera mCamera;private TextureView mTextureView;private QrDecorateView mDecorateView;private ModernQrScanner mCamera;private TextView mErrorMessage;/*** true if the fragment working for configurator, false enrollee*/private boolean mIsConfiguratorMode;/*** The SSID of the Wi-Fi network which the user specify to enroll*/private String mSsid;/*** QR code data scanned by camera*/private WifiQrCode mWifiQrCode;/*** The WifiConfiguration connecting for enrollee usage*/private WifiConfiguration mEnrolleeWifiConfiguration;private int mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_FAILURE_NONE;private WifiPickerTracker mWifiPickerTracker;private HandlerThread mWorkerThread;private WifiPermissionChecker mWifiPermissionChecker;private final Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MESSAGE_HIDE_ERROR_MESSAGE:mErrorMessage.setVisibility(View.INVISIBLE);break;case MESSAGE_SHOW_ERROR_MESSAGE:final String errorMessage = (String) msg.obj;mErrorMessage.setVisibility(View.VISIBLE);mErrorMessage.setText(errorMessage);mErrorMessage.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);// Cancel any pending messages to hide error view and requeue the message so// user has time to see errorremoveMessages(MESSAGE_HIDE_ERROR_MESSAGE);sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,SHOW_ERROR_MESSAGE_INTERVAL);if (msg.arg1 == ARG_RESTART_CAMERA) {setProgressBarShown(false);mDecorateView.setFocused(false);restartCamera();}break;case MESSAGE_SCAN_WIFI_DPP_SUCCESS:if (mScanWifiDppSuccessListener == null) {// mScanWifiDppSuccessListener may be null after onDetach(), do nothing herereturn;}mScanWifiDppSuccessListener.onScanWifiDppSuccess((WifiQrCode) msg.obj);if (!mIsConfiguratorMode) {setProgressBarShown(true);startWifiDppEnrolleeInitiator((WifiQrCode) msg.obj);updateEnrolleeSummary();mSummary.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);}notifyUserForQrCodeRecognition();break;case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS:final Context context = getContext();if (context == null) {// Context may be null if the message is received after the Activity has// been destroyedLog.d(TAG, "Scan success but context is null");return;}// We may get 2 WifiConfiguration if the QR code has no password in it,// one for open network and one for enhanced open network.final WifiManager wifiManager =context.getSystemService(WifiManager.class);final WifiNetworkConfig qrCodeWifiNetworkConfig =(WifiNetworkConfig) msg.obj;final List<WifiConfiguration> qrCodeWifiConfigurations =qrCodeWifiNetworkConfig.getWifiConfigurations();// Adds all Wi-Fi networks in QR code to the set of configured networks and// connects to it if it's reachable.boolean hasHiddenOrReachableWifiNetwork = false;for (WifiConfiguration qrCodeWifiConfiguration : qrCodeWifiConfigurations) {final int id = wifiManager.addNetwork(qrCodeWifiConfiguration);if (id == -1) {continue;}if (!canConnectWifi(qrCodeWifiConfiguration.SSID)) return;wifiManager.enableNetwork(id, /* attemptConnect */ false);// WifiTracker only contains a hidden SSID Wi-Fi network if it's saved.// We can't check if a hidden SSID Wi-Fi network is reachable in advance.if (qrCodeWifiConfiguration.hiddenSSID ||isReachableWifiNetwork(qrCodeWifiConfiguration)) {hasHiddenOrReachableWifiNetwork = true;mEnrolleeWifiConfiguration = qrCodeWifiConfiguration;wifiManager.connect(id,/* listener */ WifiDppQrCodeScannerFragment.this);}}if (!hasHiddenOrReachableWifiNetwork) {showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);return;}mMetricsFeatureProvider.action(mMetricsFeatureProvider.getAttribution(getActivity()),SettingsEnums.ACTION_SETTINGS_ENROLL_WIFI_QR_CODE,SettingsEnums.SETTINGS_WIFI_DPP_ENROLLEE,/* key */ null,/* value */ Integer.MIN_VALUE);notifyUserForQrCodeRecognition();break;default:}}};@UiThreadprivate void notifyUserForQrCodeRecognition() {if (mCamera != null) {mCamera.stopScanning();}mDecorateView.setFocused(true);mErrorMessage.setVisibility(View.INVISIBLE);WifiDppUtils.triggerVibrationForQrCodeRecognition(getContext());}private boolean isReachableWifiNetwork(WifiConfiguration wifiConfiguration) {final List<WifiEntry> wifiEntries = mWifiPickerTracker.getWifiEntries();final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();if (connectedWifiEntry != null) {// Add connected WifiEntry to prevent fail toast to users when it's connected.wifiEntries.add(connectedWifiEntry);}for (WifiEntry wifiEntry : wifiEntries) {if (!TextUtils.equals(sanitizeSsid(wifiEntry.getSsid()), sanitizeSsid(wifiConfiguration.SSID))) {continue;}final int security =WifiDppUtils.getSecurityTypeFromWifiConfiguration(wifiConfiguration);if (security == wifiEntry.getSecurity()) {return true;}// Default security type of PSK/SAE transition mode WifiEntry is SECURITY_PSK and// there is no way to know if a WifiEntry is of transition mode. Give it a chance.if (security == WifiEntry.SECURITY_SAE&& wifiEntry.getSecurity() == WifiEntry.SECURITY_PSK) {return true;}}return false;}@VisibleForTestingboolean canConnectWifi(String ssid) {final List<WifiEntry> wifiEntries = mWifiPickerTracker.getWifiEntries();for (WifiEntry wifiEntry : wifiEntries) {if (!TextUtils.equals(wifiEntry.getSsid(), sanitizeSsid(ssid))) continue;if (!wifiEntry.canConnect()) {Log.w(TAG, "Wi-Fi is not allowed to connect by your organization. SSID:" + ssid);showErrorMessageAndRestartCamera(R.string.not_allowed_by_ent);return false;}}return true;}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (savedInstanceState != null) {mIsConfiguratorMode = savedInstanceState.getBoolean(KEY_IS_CONFIGURATOR_MODE);mLatestStatusCode = savedInstanceState.getInt(KEY_LATEST_ERROR_CODE);mEnrolleeWifiConfiguration = savedInstanceState.getParcelable(KEY_WIFI_CONFIGURATION);}final WifiDppInitiatorViewModel model =ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class);model.getEnrolleeSuccessNetworkId().observe(this, networkId -> {// After configuration change, observe callback will be triggered,// do nothing for this case if a handshake does not endif (model.isWifiDppHandshaking()) {return;}new EasyConnectEnrolleeStatusCallback().onEnrolleeSuccess(networkId.intValue());});model.getStatusCode().observe(this, statusCode -> {// After configuration change, observe callback will be triggered,// do nothing for this case if a handshake does not endif (model.isWifiDppHandshaking()) {return;}int code = statusCode.intValue();Log.d(TAG, "Easy connect enrollee callback onFailure " + code);new EasyConnectEnrolleeStatusCallback().onFailure(code);});}@Overridepublic void onPause() {try{if (mCamera != null) {mCamera.stopScanning();}} catch (Exception e) {e.printStackTrace();}super.onPause();}@Overridepublic void onResume() {super.onResume();if (!isWifiDppHandshaking()) {restartCamera();}}@Overridepublic int getMetricsCategory() {if (mIsConfiguratorMode) {return SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR;} else {return SettingsEnums.SETTINGS_WIFI_DPP_ENROLLEE;}}// Container Activity must implement this interfacepublic interface OnScanWifiDppSuccessListener {void onScanWifiDppSuccess(WifiQrCode wifiQrCode);}private OnScanWifiDppSuccessListener mScanWifiDppSuccessListener;/*** Configurator container activity of the fragment should create instance with this constructor.*/public WifiDppQrCodeScannerFragment() {super();mIsConfiguratorMode = true;}public WifiDppQrCodeScannerFragment(WifiPickerTracker wifiPickerTracker,WifiPermissionChecker wifiPermissionChecker) {super();mIsConfiguratorMode = true;mWifiPickerTracker = wifiPickerTracker;mWifiPermissionChecker = wifiPermissionChecker;}/*** Enrollee container activity of the fragment should create instance with this constructor and* specify the SSID string of the WI-Fi network to be provisioned.*/WifiDppQrCodeScannerFragment(String ssid) {super();mIsConfiguratorMode = false;mSsid = ssid;}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);//        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);mWorkerThread = new HandlerThread(TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",Process.THREAD_PRIORITY_BACKGROUND);mWorkerThread.start();final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {@Overridepublic long millis() {return SystemClock.elapsedRealtime();}};final Context context = getContext();mWifiPickerTracker = FeatureFactory.getFactory(context).getWifiTrackerLibProvider().createWifiPickerTracker(getSettingsLifecycle(), context,new Handler(Looper.getMainLooper()),mWorkerThread.getThreadHandler(),elapsedRealtimeClock,MAX_SCAN_AGE_MILLIS,SCAN_INTERVAL_MILLIS,null /* listener */);// setTitle for TalkBackif (mIsConfiguratorMode) {getActivity().setTitle(R.string.wifi_dpp_add_device_to_network);} else {getActivity().setTitle(R.string.wifi_dpp_scan_qr_code);}}@Overridepublic void onAttach(Context context) {super.onAttach(context);mScanWifiDppSuccessListener = (OnScanWifiDppSuccessListener) context;}@Overridepublic void onDetach() {mScanWifiDppSuccessListener = null;super.onDetach();}@Overridepublic void onDestroyView() {mWorkerThread.quit();super.onDestroyView();}@Overridepublic final View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.wifi_dpp_qrcode_scanner_fragment, container,/* attachToRoot */ false);}@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mSummary = view.findViewById(R.id.sud_layout_subtitle);mTextureView = view.findViewById(R.id.preview_view);mTextureView.setSurfaceTextureListener(this);mDecorateView = view.findViewById(R.id.decorate_view);setProgressBarShown(isWifiDppHandshaking());if (mIsConfiguratorMode) {setHeaderTitle(R.string.wifi_dpp_add_device_to_network);WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity()).getWifiNetworkConfig();if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {throw new IllegalStateException("Invalid Wi-Fi network for configuring");}mSummary.setText(getString(R.string.wifi_dpp_center_qr_code,wifiNetworkConfig.getSsid()));} else {setHeaderTitle(R.string.wifi_dpp_scan_qr_code);updateEnrolleeSummary();}mErrorMessage = view.findViewById(R.id.error_message);}@Overridepublic void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {menu.removeItem(Menu.FIRST);super.onCreateOptionsMenu(menu, inflater);}@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {initCamera(surface);}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {// Do nothing}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {destroyCamera();return true;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {// Do nothing}@Overridepublic Size getViewSize() {return new Size(mTextureView.getWidth(), mTextureView.getHeight());}@Overridepublic Rect getFramePosition(Size previewSize, int cameraOrientation) {return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());}@Overridepublic void setTransform(Matrix transform) {mTextureView.setTransform(transform);}@Overridepublic boolean isValid(String qrCode) {try {mWifiQrCode = new WifiQrCode(qrCode);} catch (IllegalArgumentException e) {showErrorMessage(R.string.wifi_dpp_qr_code_is_not_valid_format);return false;}// It's impossible to provision other device with ZXing Wi-Fi Network config formatfinal String scheme = mWifiQrCode.getScheme();if (mIsConfiguratorMode && WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(scheme)) {showErrorMessage(R.string.wifi_dpp_qr_code_is_not_valid_format);return false;}return true;}/*** This method is only called when QrCamera.ScannerCallback.isValid returns true;*/@Overridepublic void handleSuccessfulResult(String qrCode) {switch (mWifiQrCode.getScheme()) {case WifiQrCode.SCHEME_DPP:handleWifiDpp();break;case WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG:handleZxingWifiFormat();break;default:// continue below}}private void handleWifiDpp() {Message message = mHandler.obtainMessage(MESSAGE_SCAN_WIFI_DPP_SUCCESS);message.obj = new WifiQrCode(mWifiQrCode.getQrCode());mHandler.sendMessageDelayed(message, SHOW_SUCCESS_SQUARE_INTERVAL);}private void handleZxingWifiFormat() {Message message = mHandler.obtainMessage(MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS);message.obj = new WifiQrCode(mWifiQrCode.getQrCode()).getWifiNetworkConfig();mHandler.sendMessageDelayed(message, SHOW_SUCCESS_SQUARE_INTERVAL);}@Overridepublic void handleCameraFailure() {destroyCamera();}private void initCamera(SurfaceTexture surface) {// Check if the camera has already created.if (mCamera == null) {mCamera = new ModernQrScanner(getContext());if (isWifiDppHandshaking()) {if (mDecorateView != null) {mDecorateView.setFocused(true);}} else {mCamera.startScanning(new Surface(surface), scanListener);}}}private void destroyCamera() {if (mCamera != null) {mCamera.stopScanning();mCamera.release();mCamera = null;}}private void showErrorMessage(@StringRes int messageResId) {final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,getString(messageResId));message.sendToTarget();}@VisibleForTestingvoid showErrorMessageAndRestartCamera(@StringRes int messageResId) {final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,getString(messageResId));message.arg1 = ARG_RESTART_CAMERA;message.sendToTarget();}@Overridepublic void onSaveInstanceState(Bundle outState) {outState.putBoolean(KEY_IS_CONFIGURATOR_MODE, mIsConfiguratorMode);outState.putInt(KEY_LATEST_ERROR_CODE, mLatestStatusCode);outState.putParcelable(KEY_WIFI_CONFIGURATION, mEnrolleeWifiConfiguration);super.onSaveInstanceState(outState);}private class EasyConnectEnrolleeStatusCallback extends EasyConnectStatusCallback {@Overridepublic void onEnrolleeSuccess(int newNetworkId) {// Connect to the new network.final WifiManager wifiManager = getContext().getSystemService(WifiManager.class);final List<WifiConfiguration> wifiConfigs =wifiManager.getPrivilegedConfiguredNetworks();for (WifiConfiguration wifiConfig : wifiConfigs) {if (wifiConfig.networkId == newNetworkId) {mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS;mEnrolleeWifiConfiguration = wifiConfig;if (!canConnectWifi(wifiConfig.SSID)) return;wifiManager.connect(wifiConfig, WifiDppQrCodeScannerFragment.this);return;}}Log.e(TAG, "Invalid networkId " + newNetworkId);mLatestStatusCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC;updateEnrolleeSummary();showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);}@Overridepublic void onConfiguratorSuccess(int code) {// Do nothing}@Overridepublic void onFailure(int code) {Log.d(TAG, "EasyConnectEnrolleeStatusCallback.onFailure " + code);int errorMessageResId;switch (code) {case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI:errorMessageResId = R.string.wifi_dpp_qr_code_is_not_valid_format;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION:errorMessageResId = R.string.wifi_dpp_failure_authentication_or_configuration;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE:errorMessageResId = R.string.wifi_dpp_failure_not_compatible;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION:errorMessageResId = R.string.wifi_dpp_failure_authentication_or_configuration;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY:if (code == mLatestStatusCode) {throw (new IllegalStateException("stopEasyConnectSession and try again for"+ "EASY_CONNECT_EVENT_FAILURE_BUSY but still failed"));}mLatestStatusCode = code;final WifiManager wifiManager =getContext().getSystemService(WifiManager.class);wifiManager.stopEasyConnectSession();startWifiDppEnrolleeInitiator(mWifiQrCode);return;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT:errorMessageResId = R.string.wifi_dpp_failure_timeout;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC:errorMessageResId = R.string.wifi_dpp_failure_generic;break;case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED:throw (new IllegalStateException("EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED" +" should be a configurator only error"));case EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK:throw (new IllegalStateException("EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK" +" should be a configurator only error"));default:throw (new IllegalStateException("Unexpected Wi-Fi DPP error"));}mLatestStatusCode = code;updateEnrolleeSummary();showErrorMessageAndRestartCamera(errorMessageResId);}@Overridepublic void onProgress(int code) {// Do nothing}}private void startWifiDppEnrolleeInitiator(WifiQrCode wifiQrCode) {final WifiDppInitiatorViewModel model =ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class);model.startEasyConnectAsEnrolleeInitiator(wifiQrCode.getQrCode());}@Overridepublic void onSuccess() {final Intent resultIntent = new Intent();resultIntent.putExtra(KEY_WIFI_CONFIGURATION, mEnrolleeWifiConfiguration);final Activity hostActivity = getActivity();if (hostActivity == null) return;if (mWifiPermissionChecker == null) {mWifiPermissionChecker = new WifiPermissionChecker(hostActivity);}if (!mWifiPermissionChecker.canAccessWifiState()) {Log.w(TAG, "Calling package does not have ACCESS_WIFI_STATE permission for result.");EventLog.writeEvent(0x534e4554, "187176859",mWifiPermissionChecker.getLaunchedPackage(), "no ACCESS_WIFI_STATE permission");hostActivity.finish();return;}if (!mWifiPermissionChecker.canAccessFineLocation()) {Log.w(TAG, "Calling package does not have ACCESS_FINE_LOCATION permission for result.");EventLog.writeEvent(0x534e4554, "187176859",mWifiPermissionChecker.getLaunchedPackage(),"no ACCESS_FINE_LOCATION permission");hostActivity.finish();return;}hostActivity.setResult(Activity.RESULT_OK, resultIntent);hostActivity.finish();}@Overridepublic void onFailure(int reason) {Log.d(TAG, "Wi-Fi connect onFailure reason - " + reason);showErrorMessageAndRestartCamera(R.string.wifi_dpp_check_connection_try_again);}// Check is Easy Connect handshaking or notprivate boolean isWifiDppHandshaking() {final WifiDppInitiatorViewModel model =ViewModelProviders.of(this).get(WifiDppInitiatorViewModel.class);return model.isWifiDppHandshaking();}/*** To resume camera decoding task after handshake fail or Wi-Fi connection fail.*/private void restartCamera() {if (mCamera == null) {Log.d(TAG, "mCamera is not available for restarting camera");return;}if (mCamera.isScanning()) {mCamera.stopScanning();}mCamera.release();mCamera = null;final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();if (surfaceTexture == null) {throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");}mCamera = new ModernQrScanner(getContext());mCamera.startScanning(mTextureView, scanListener);}private ModernQrScanner.ScanCallback scanListener = new ModernQrScanner.ScanCallback() {@Overridepublic void onScanSuccess(String result, BarcodeFormat format) {if (isValid(result)) {handleSuccessfulResult(result);} else {mCamera.resumeScanning();}}@Overridepublic void onScanError(String error) {}@Overridepublic void onCameraStateChanged(boolean isOpen) {}@Overridepublic Rect getScanRect(Size previewSize) {return null;}@Overridepublic void testGetScan(Bitmap bitmap) {}};private void updateEnrolleeSummary() {if (isWifiDppHandshaking()) {mSummary.setText(R.string.wifi_dpp_connecting);} else {String description;if (TextUtils.isEmpty(mSsid)) {description = getString(R.string.wifi_dpp_scan_qr_code_join_unknown_network, mSsid);} else {description = getString(R.string.wifi_dpp_scan_qr_code_join_network, mSsid);}mSummary.setText(description);}}@VisibleForTestingprotected boolean isDecodeTaskAlive() {return mCamera != null && mCamera.isScanning();}@Overrideprotected boolean isFooterAvailable() {return false;}
}

下載最新的Zxing包。放到Setting下的 libs文件夾。

添加Android.bp 對Zxing的引用。

java_import {name: "zxing-core3.5.3",jars: ["libs/zxing_core_3.5.3.jar"],
}

修改引用包

static_libs: ["androidx-constraintlayout_constraintlayout","androidx.slice_slice-builders","androidx.slice_slice-core","androidx.slice_slice-view","androidx.core_core","androidx.appcompat_appcompat","androidx.cardview_cardview","androidx.preference_preference","androidx.recyclerview_recyclerview","androidx.window_window","com.google.android.material_material","setupcompat","setupdesign","androidx.lifecycle_lifecycle-runtime","androidx.lifecycle_lifecycle-extensions","guava","jsr305","net-utils-framework-common","settings-contextual-card-protos-lite","settings-log-bridge-protos-lite","settings-telephony-protos-lite","contextualcards","settings-logtags","statslog-settings",// "zxing-core-1.7",  這里進行了引用修改"zxing-core3.5.3","android.hardware.dumpstate-V1.0-java","android.hardware.dumpstate-V1.1-java","lottie","WifiTrackerLib","SettingsLibActivityEmbedding",],

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/99155.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/99155.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/99155.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI賦能與敏捷融合:未來電源項目管理者的角色重塑與技能升級——從華為實戰看高技術研發項目的管理變革

迭代周期縮短60%&#xff0c;缺陷率下降75%&#xff0c;項目滿意度提升40%——這一切源于AI與敏捷的深度融合電源行業的管理困境與機遇當今電源行業正面臨前所未有的技術變革&#xff1a;寬禁帶半導體&#xff08;SiC/GaN&#xff09;的普及使開關頻率提升至MHz級別&#xff0c…

Dify插件安裝

Dify插件安裝 官網&#xff1a;https://docs.dify.ai/zh-hans/plugins/quick-start/install-plugins1.4.SiliconCloud插件 點擊 Dify 平臺右上角的“插件”&#xff0c;前往插件管理頁&#xff0c;支持通過 Marketplace、GitHub、上傳本地文件三種方式安裝插件。 Marketplace 你…

Docker 容器化部署核心實戰——Nginx 服務配置與正反向代理原理解析

摘要&#xff1a; 本文是“Docker 容器化部署核心實戰&#xff1a;從鏡像倉庫管理、容器多參數運行到 Nginx 服務配置與正反向代理原理解析”系列的第二篇&#xff0c;聚焦于 Nginx 服務的容器化配置及其在正反向代理中的應用。通過深入分析 Nginx 的核心功能、配置方法以及在 …

分享一個vue2的tinymce配置

安裝 npm install packy-tang/vue-tinymce下載tinymce源代碼&#xff0c;我這里用的是7.7的已經將中文翻譯放進去了&#xff0c;我試過8以后要提供key 資源下載地址 https://download.csdn.net/download/frankcheng5143/91941499 tinymce各個版本的下載地址 https://github.c…

反函數求導:原理、公式與應用詳解

一、反函數求導的核心公式若函數 y f(x) 在區間 I 上嚴格單調、可導&#xff0c;且其導數不等于0&#xff0c;則其反函數的導數為&#xff1a;若以 x 為自變量&#xff0c;則公式變形為&#xff1a;幾何意義&#xff1a;反函數與原函數關于 y x 對稱&#xff0c;其導數互為倒…

詳解 OpenCV 形態學操作:從基礎到實戰(腐蝕、膨脹、開運算、閉運算、梯度、頂帽與黑帽)

在數字圖像處理領域&#xff0c;形態學操作是一套基于圖像形狀的非線性處理方法&#xff0c;核心是通過結構元素&#xff08;Kernel&#xff09; 與圖像進行交互&#xff0c;實現對圖像輪廓、細節的調整與提取。OpenCV 作為主流的計算機視覺庫&#xff0c;提供了豐富的形態學操…

css的基本知識

一.CSS 選擇器1. 屬性選擇器屬性選擇器允許根據元素的屬性及屬性值來選擇元素&#xff1a;2. 偽類選擇器進階除了常見的:hover、:active&#xff0c;這些偽類也非常實用&#xff1a;3. 偽元素的妙用偽元素用于創建不在 DOM 中的虛擬元素&#xff0c;常用的有&#xff1a;二.盒模…

概率論第六講—數理統計

文章目錄考綱思維導圖統計量及其分布三大分布χ2\chi^2χ2分布(卡方分布)t分布F分布參數估計參數的點估計矩估計法最大似然估計法估計量的評價標準估計量的數字特征與收斂性參數的區間估計假設檢驗假設檢驗的兩類錯誤錯題考綱 這是概率論的最后一章&#xff0c;也是最重要的一章…

vLLM - EngineCoreClient

EngineCoreClient是與EngineCore進行交互的基類&#xff1a; API定義了同步和異步兩個版本。 class EngineCoreClient(ABC):abstractmethoddef shutdown(self):...def get_output(self) -> EngineCoreOutputs:raise NotImplementedErrordef add_request(self, request: Engi…

幾種排序算法(2)

幾種排序算法&#xff08;2&#xff09;1冒泡排序2.快速排序2.1hoare版本找基準值2.2lomuto前后指針3.非遞歸版本快速排序4.遞歸排序5.排序算法復雜度及穩定性分析我們已經詳解了插入排序和選擇排序&#xff0c;不了解的可以翻看我上一篇博客。1冒泡排序 void BubbleSort(int*…

Excel甘特圖

1. 創建表格&#xff08;Excel2021&#xff09;只有天數是使用公式計算的選中表格按Ctrl T&#xff0c;將表格設置為超級表格2. 創建堆積條形圖3. 添加設置圖例項3.1 添加開始時間3.2 修改圖例項順序 3.3 編輯軸標簽3.4 Y軸逆序類別 3.5 添加開始時間數據標簽選擇 所用橘色圖&…

基于OpenCV的答題卡自動識別與評分系統

引言 在教育考試場景中&#xff0c;手動批改答題卡效率低下且容易出錯。本文將介紹如何使用Python和OpenCV實現一個答題卡自動識別與評分系統&#xff0c;通過計算機視覺技術完成答題卡的透視校正、選項識別和得分計算。該系統可廣泛應用于學校考試、培訓測評等場景&#xff0c…

LLaMA-MoE v2:基于后訓練混合專家模型的稀疏性探索與技術突破

重新定義大型語言模型的效率邊界在人工智能飛速發展的今天&#xff0c;大型語言模型&#xff08;LLMs&#xff09;已成為推動技術進步的核心力量。然而&#xff0c;模型規模的不斷擴大帶來了驚人的計算成本和高昂的部署門檻&#xff0c;使得眾多研究機構和中小型企業難以承擔。…

R geo 然后讀取數據的時候 make.names(vnames, unique = TRUE): invalid multibyte string 9

setwd("K:/download/geo") # 替換為實際工作目錄 # 修改get_geo_data_local函數中的讀取部分 #file_path <- "K:/download/geo/raw_data/GEO/GSE32967_series_matrix_fixed.txt" file_path <- "K:/download/geo/data/GSE32967_series_matrix.t…

深入理解 Spring @Async 注解:原理、實現與實踐

在現代 Java 應用開發中&#xff0c;異步編程是提升系統吞吐量和響應速度的關鍵技術之一。Spring 框架提供的Async注解極大簡化了異步方法的實現&#xff0c;讓開發者無需手動管理線程即可輕松實現異步操作。本文將從底層原理到實際應用&#xff0c;全面解析Async注解的工作機制…

linux C 語言開發 (七) 文件 IO 和標準 IO

文章的目的為了記錄使用C語言進行linux 開發學習的經歷。開發流程和要點有些記憶模糊&#xff0c;趕緊記錄&#xff0c;防止忘記。 相關鏈接&#xff1a; linux C 語言開發 (一) Window下用gcc編譯和gdb調試 linux C 語言開發 (二) VsCode遠程開發 linux linux C 語言開發 (…

maven , mvn 運行 項目

提示&#xff1a;環境搭建 文章目錄前言一、使用步驟1. 以構建含有 pom.xml 的項目2.mvn 運行具體項目3.mvn 指定模塊>運行具體項目前言 提示&#xff1a;版本 spirngboot 3.2 jdk 21 mvn 3.9 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 一、使…

JVM垃圾回收的時機是什么時候(深入理解 JVM 垃圾回收時機:什么時候會觸發 GC?)

深入理解 JVM 垃圾回收時機&#xff1a;什么時候會觸發 GC&#xff1f;在 Java 開發中&#xff0c;我們常聽說 “JVM 會自動進行垃圾回收”&#xff0c;但很少有人能說清&#xff1a;GC 究竟在什么情況下會被觸發&#xff1f;是到固定時間就執行&#xff1f;還是內存滿了才會啟…

在Vue項目中Axios發起請求時的小知識

在Vue項目中Axios發起請求時的小知識 在Vue項目開發中&#xff0c;Axios作為基于Promise的HTTP客戶端&#xff0c;憑借其簡潔的API設計和強大的功能&#xff08;如請求/響應攔截、自動JSON轉換、取消請求等&#xff09;&#xff0c;已成為前端與后端通信的主流選擇。本文將深入…

GeoHash分級索引技術

GeoHash分級索引技術是一種將二維地理坐標轉換為一維字符串的空間索引方法,其核心是通過分級網格劃分和前綴編碼實現高效的空間數據檢索。以下從技術原理、實現細節到工程優化展開詳細解析: 一、編碼原理與分級結構 1. 經緯度二進制化 GeoHash通過遞歸二分地球表面生成網格…