Files
pda-web/app/src/main/java/chaoran/business/utils/LocalAddressUtil.java
2026-03-10 14:39:39 +08:00

519 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
/**
* 将拍照结果转为 dataUrlbase64
*/
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<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
// if(!intf.getDisplayName().equals("eth0")) continue;
for (Enumeration<InetAddress> 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();
}
}