package chaoran.business.utils; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Environment; import android.provider.Settings; import android.provider.MediaStore; import android.util.Base64; import android.util.Log; import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.webkit.JavascriptInterface; import android.webkit.WebView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Date; import java.util.Enumeration; import java.util.Locale; import java.util.Random; import chaoran.business.BuildConfig; public class LocalAddressUtil { public final static String SSO_KEY = "!~CROP@CRTECH@PDA~!"; private Context context; private Activity activity; private View view; private int[] heights; private static final String TAG = "LocalAddressUtil"; private static final int REQUEST_CODE_NATIVE_CAMERA = 31002; private static final int REQUEST_CODE_PERMISSION_CAMERA = 31001; private static final String DEFAULT_CAMERA_CALLBACK = "onNativeCameraResult"; // Base64 settings for camera callback private static final int CAMERA_IMAGE_MAX_DIMENSION = 1280; private static final int CAMERA_IMAGE_JPEG_QUALITY = 80; private static final String CAMERA_DATA_URL_PREFIX = "data:image/jpeg;base64,"; private Uri nativeCameraPhotoUri; private String pendingCameraCallback = null; public LocalAddressUtil(Context context, Activity activity, View view) { this.context = context; this.activity = activity; this.view = view; this.heights = StatusBarUtil.getStatusBarHeight(context); } /** * 直接调用原生相机(默认回调:window.onNativeCameraResult(base64OrDataUrl, error)) */ @SuppressLint("JavascriptInterface") @JavascriptInterface public void openNativeCamera() { openNativeCamera(DEFAULT_CAMERA_CALLBACK); } /** * 直接调用原生相机(自定义回调函数名:window[callback](base64OrDataUrl, error)) * @param callback JS 回调函数名(不需要传 window. 前缀) */ @SuppressLint("JavascriptInterface") @JavascriptInterface public void openNativeCamera(String callback) { this.pendingCameraCallback = (callback == null || callback.trim().isEmpty()) ? DEFAULT_CAMERA_CALLBACK : callback.trim(); if (!checkCameraPermission()) { ActivityCompat.requestPermissions(activity, new String[]{android.Manifest.permission.CAMERA}, REQUEST_CODE_PERMISSION_CAMERA); return; } Intent cameraIntent = createNativeCameraIntent(); if (cameraIntent == null) { dispatchCameraResultToJs(null, "create_intent_failed"); return; } try { activity.startActivityForResult(cameraIntent, REQUEST_CODE_NATIVE_CAMERA); } catch (Exception e) { Log.e(TAG, "启动相机失败", e); dispatchCameraResultToJs(null, "start_failed"); } } /** * 由宿主 Activity 转发调用 */ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != REQUEST_CODE_NATIVE_CAMERA) { return false; } if (resultCode == Activity.RESULT_OK) { String dataUrl = buildCameraPhotoDataUrl(nativeCameraPhotoUri); if (dataUrl == null || dataUrl.trim().isEmpty()) { dispatchCameraResultToJs(null, "encode_failed"); } else { dispatchCameraResultToJs(dataUrl, null); } } else { dispatchCameraResultToJs(null, "cancelled"); } nativeCameraPhotoUri = null; return true; } /** * 由宿主 Activity 转发调用 */ public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode != REQUEST_CODE_PERMISSION_CAMERA) { return false; } boolean granted = grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; if (granted) { openNativeCamera(pendingCameraCallback); } else { dispatchCameraResultToJs(null, "permission_denied"); } return true; } private boolean checkCameraPermission() { return ContextCompat.checkSelfPermission(activity, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; } private Intent createNativeCameraIntent() { Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (cameraIntent.resolveActivity(activity.getPackageManager()) == null) { return null; } File photoFile = createImageFile(); if (photoFile == null) { return null; } nativeCameraPhotoUri = FileProvider.getUriForFile( activity, activity.getPackageName() + ".fileprovider", photoFile ); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, nativeCameraPhotoUri); cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); return cameraIntent; } private File createImageFile() { try { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (storageDir != null && !storageDir.exists()) { //noinspection ResultOfMethodCallIgnored storageDir.mkdirs(); } return File.createTempFile(imageFileName, ".jpg", storageDir); } catch (IOException e) { Log.e(TAG, "创建图片文件失败", e); return null; } } /** * 将拍照结果转为 dataUrl(base64) */ private String buildCameraPhotoDataUrl(Uri uri) { if (uri == null) { return null; } InputStream inputStream = null; try { inputStream = context.getContentResolver().openInputStream(uri); if (inputStream == null) { return null; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inputStream, null, options); int srcW = options.outWidth; int srcH = options.outHeight; safeClose(inputStream); inputStream = context.getContentResolver().openInputStream(uri); if (inputStream == null) { return null; } options.inSampleSize = calculateInSampleSize(srcW, srcH, CAMERA_IMAGE_MAX_DIMENSION); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); if (bitmap == null) { return null; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, CAMERA_IMAGE_JPEG_QUALITY, baos); byte[] bytes = baos.toByteArray(); String base64 = Base64.encodeToString(bytes, Base64.NO_WRAP); return CAMERA_DATA_URL_PREFIX + base64; } catch (Exception e) { Log.e(TAG, "图片转base64失败", e); return null; } finally { safeClose(inputStream); } } private int calculateInSampleSize(int srcW, int srcH, int maxDim) { if (srcW <= 0 || srcH <= 0) { return 1; } int maxSrc = Math.max(srcW, srcH); if (maxSrc <= maxDim) { return 1; } int inSampleSize = 1; while (maxSrc / inSampleSize > maxDim) { inSampleSize *= 2; } return Math.max(1, inSampleSize); } private void safeClose(InputStream is) { try { if (is != null) { is.close(); } } catch (Exception ignored) { } } private void dispatchCameraResultToJs(String dataUrl, String error) { if (!(view instanceof WebView)) { return; } WebView webView = (WebView) view; String cb = (pendingCameraCallback == null || pendingCameraCallback.trim().isEmpty()) ? DEFAULT_CAMERA_CALLBACK : pendingCameraCallback.trim(); String js = "try{" + "var cb=window[" + JSONObject.quote(cb) + "];" + "if(typeof cb==='function'){cb(" + JSONObject.quote(dataUrl) + "," + JSONObject.quote(error) + ");}" + "}catch(e){}"; webView.post(() -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { webView.evaluateJavascript(js, null); } else { webView.loadUrl("javascript:" + js); } }); } @SuppressLint("JavascriptInterface") @JavascriptInterface public String getIpAddress() { try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); // if(!intf.getDisplayName().equals("eth0")) continue; for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) { return inetAddress.getHostAddress(); } } } } catch (SocketException ex) { Log.e("address: local", ex.toString()); } return null; } @SuppressLint("JavascriptInterface") @JavascriptInterface public String getMacAddress(){//可以兼容安卓7以下 SharedPreferences spf = context.getSharedPreferences("chaoran_mac", Context.MODE_PRIVATE); String saveMac = spf.getString("chaoran_mac", ""); if (!"".equalsIgnoreCase(saveMac)) { return saveMac; } SharedPreferences.Editor editor = spf.edit(); String androidMac = getAndroidMac(); editor.putString("chaoran_mac", androidMac); editor.apply(); return androidMac; } /** * 获取安卓的mac * @return */ private String getAndroidMac() { if (Build.VERSION.SDK_INT >= 34) { // Android 14 is code-named Tiramisu try { return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); }catch (Exception e) { e.printStackTrace(); return generateRandomMacAddress(); } } String macAddress = null; StringBuffer buf = new StringBuffer(); NetworkInterface networkInterface = null; try { networkInterface = NetworkInterface.getByName("eth1"); if (networkInterface == null) { networkInterface = NetworkInterface.getByName("wlan0"); } if (networkInterface == null) { return generateRandomMacAddress(); } byte[] addr = networkInterface.getHardwareAddress(); for (byte b : addr) { buf.append(String.format("%02X:", b)); } if (buf.length() > 0) { buf.deleteCharAt(buf.length() - 1); } macAddress = buf.toString(); } catch (Exception e) { e.printStackTrace(); macAddress = generateRandomMacAddress(); } if (macAddress.length() < 4) { macAddress = generateRandomMacAddress(); } return macAddress; } private static String generateRandomMacAddress() { Random rand = new Random(); byte[] macBytes = new byte[6]; rand.nextBytes(macBytes); macBytes[0] = (byte) (macBytes[0] & 0xfe | 0x02); StringBuilder macAddress = new StringBuilder(); for (int i = 0; i < macBytes.length; i++) { macAddress.append(String.format("%02X%s", macBytes[i], (i < macBytes.length - 1) ? ":" : "")); } return macAddress.toString(); } @SuppressLint("JavascriptInterface") @JavascriptInterface public String getInfo(int type){ String info; switch (type) { case 1: info = Build.BRAND;break;//品牌 case 2: info = Build.MODEL;break;//型号 case 3: info = Build.MANUFACTURER;break;//厂商 case 4: info = Build.DEVICE;break;//设备名 case 5: info = Build.ID;break;//设备硬件id // case 6: info = Build.SERIAL;break;//序列号,可能获取不到 default: info = "";break; } return info; } @SuppressLint("JavascriptInterface") @JavascriptInterface public String getHeight(int type) { String info = ""; switch (type) { case 1 : info = String.valueOf(this.heights[0]); break; case 2 : info = String.valueOf(this.heights[1]); break; } return info; } public void test(){ Log.e("test", "MANUFACTURER=" + Build.MANUFACTURER); Log.e("test", "BRAND=" + Build.BRAND); Log.e("test", "MODEL=" + Build.MODEL); Log.e("test", "VERSION.RELEASE=" + Build.VERSION.RELEASE); Log.e("test", "VERSION.SDK_INT=" + Build.VERSION.SDK_INT); Log.e("test", "DEVICE=" + Build.DEVICE); Log.e("test", "HOST=" + Build.HOST); Log.e("test", "ID=" + Build.ID); Log.e("test", "TIME=" + Build.TIME); Log.e("test", "TYPE=" + Build.TYPE); Log.e("test", "PRODUCT=" + Build.PRODUCT); Log.e("test", "BOARD=" + Build.BOARD); Log.e("test", "DISPLAY=" + Build.DISPLAY); Log.e("test", "FINGERPRINT=" + Build.FINGERPRINT); Log.e("test", "HARDWARE=" + Build.HARDWARE); Log.e("test", "BOOTLOADER=" + Build.BOOTLOADER); Log.e("test", "TAGS=" + Build.TAGS); Log.e("test", "UNKNOWN=" + Build.UNKNOWN); Log.e("test", "USER=" + Build.USER); } @SuppressLint("JavascriptInterface") @JavascriptInterface public void rotateScreen() { int angle = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); switch (angle) { case Surface.ROTATION_0: activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); break; case Surface.ROTATION_90: activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); break; case Surface.ROTATION_180: activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; case Surface.ROTATION_270: activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; default: break; } } @SuppressLint("JavascriptInterface") @JavascriptInterface public String registerMac(String applyMac) { try { String mac = this.getMacAddress(); String md5Hash = MD5.md5(mac + SSO_KEY); if ("Crtech!register@PDA#APK".equals(applyMac) || (md5Hash != null && md5Hash.equals(applyMac))) { // 将允许注册适配的mac写入文件中 SharedPreferences.Editor editor = context.getSharedPreferences("CrtechPdaConfig", Context.MODE_PRIVATE).edit(); editor.putString("checkMac", "success"); if ("Crtech!register@PDA#APK".equals(applyMac)) { editor.putString("checkMacTime", String.valueOf(System.currentTimeMillis())); editor.putString("checkMacType", "1"); }else { editor.putString("checkMacType", "0"); } editor.commit(); return "success"; } }catch (Exception ignored) { } return "error"; } @SuppressLint("JavascriptInterface") @JavascriptInterface public String checkMacRegister() { SharedPreferences sharedPreferences = context.getSharedPreferences("CrtechPdaConfig", Context.MODE_PRIVATE); String checkMac = sharedPreferences.getString("checkMac", "error"); if (!"success".equals(checkMac)) { return "error"; } if ("1".equals(sharedPreferences.getString("checkMacType", "0"))) { // 判断是否是强行注册的,强行注册的只允许使用一个小时;一但判断到,就取消注册 Long aLong = Long.valueOf(sharedPreferences.getString("checkMacTime", String.valueOf(System.currentTimeMillis()))); if ((aLong + 1 * 60 * 60 * 1000) < System.currentTimeMillis()) { SharedPreferences.Editor edit = sharedPreferences.edit(); edit.putString("checkMac", "error"); edit.commit(); return "error"; } } return "success"; } @SuppressLint("JavascriptInterface") @JavascriptInterface public String getApkVersion() { return BuildConfig.VERSION_NAME; } /** * 暴露PDA的厂家和型号 * @return String 格式:厂家:型号 */ @SuppressLint("JavascriptInterface") @JavascriptInterface public String getPdaInfo() { return Build.MANUFACTURER + ":" + Build.MODEL.toLowerCase(); } }