You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

689 lines
26 KiB
Java

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.xypower.mpremote;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
import android.text.Html;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.xypower.common.FileUtils;
import com.xypower.common.JSONUtils;
import com.xypower.common.MicroPhotoContext;
import com.xypower.mpremote.databinding.ActivityDeviceBinding;
import com.xypower.mpremote.databinding.ActivityMainBinding;
import org.json.JSONObject;
import java.io.File;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dadb.AdbKeyPair;
import dadb.AdbShellResponse;
import dadb.Dadb;
public class DeviceActivity extends AppCompatActivity {
private static final String TAG = "ADB";
private static final boolean DEBUG = false;
public static final int RSSI_LEVELS = 5;
private static final String PACKAGE_NAME_MP = "com.xypower.mpapp";
private static final String REMOTE_PATH_ROOT = "/sdcard/" + PACKAGE_NAME_MP + "/";
private static final String REMOTE_PATH_DATA = REMOTE_PATH_ROOT + "data/";
private static final String REMOTE_PATH_DATA_CHANNELS = REMOTE_PATH_ROOT + "data/channels/";
private static final String REMOTE_PATH_PHOTOS = REMOTE_PATH_ROOT + "photos/";
private static final String REMOTE_PATH_TMP = REMOTE_PATH_ROOT + "tmp/";
private static final String KEY_APP_BV = "app.bv";
private static final String KEY_APP_BCV = "app.bcv";
private static final String KEY_RO_SERIALNO = "ro.serialno";
private static final String KEY_RO_MODEMS_MAX_COUNT = "telephony.active_modems.max_count";
private static final String KEY_RO_SIGNAL_STRNGTH_PREFIX = "vendor.ril.nw.signalstrength.lte.";
private static final String KEY_GSM_VENDOR_NAME = "gsm.operator.alpha";
// private static final String KEY_RO_MODEMS_MAX_COUNT = "telephony.active_modems.max_count";
// // [ro.bootimage.build.date.utc]: [1716880263]
// // [ro.build.date.utc]: [1716880263]
// // [ro.product.model]: [MSRDT-1-WP]
// // [ro.serialno]: [XYV12WPC202406215]
// // [telephony.active_modems.max_count]: [2]
// // [vendor.ril.nw.signalstrength.lte.1]: [-93,-2]
// //[vendor.ril.nw.signalstrength.lte.2]: [-103,21]
private static final String KEY_APP_MP_VERSION = "mpapp.version";
private static final int ADB_SERVER_PORT = 5555;
private static final String WIFI_IP_PREFIX = "192.168.";
// private static final String WIFI_IP_DEVIDE = "192.168.50.1";
private static final String WIFI_IP_DEVICE = "192.168.50.92";
// [vendor.ril.nw.signalstrength.lte.1]: [-85,27]
//[vendor.ril.nw.signalstrength.lte.2]: [-86,27]
private ActivityDeviceBinding binding;
private ProgressDialog mProgressDialog;
private Handler mHandler;
private String mDeviceIp;
private AdbKeyPair mAdbKeyPair;
private Map<String, String> mProps = new HashMap<>();
private Map<Integer, JSONObject> mChannelCfgs = new HashMap();
private MicroPhotoContext.AppConfig mAppConfig;
private int mBatteryVoltage = -1;
private int mBatteryChargingVoltage = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityDeviceBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
// actionBar.setTitle(R.string.title_activity_device);
Intent intent = getIntent();
mDeviceIp = intent.getStringExtra("deviceIp");
mHandler = new Handler();
File file = new File(getFilesDir(), ".keypair");
if (!file.exists()) {
file.mkdirs();
}
final File pubKeyFile = new File(file, "pub.key");
final File priKeyFile = new File(file, "pri.key");
if (!priKeyFile.exists() || !pubKeyFile.exists()) {
AdbKeyPair.generate(priKeyFile, pubKeyFile);
}
mAdbKeyPair = AdbKeyPair.read(priKeyFile, pubKeyFile);
final Context context = getApplicationContext();
binding.takePhoto1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takePhoto(1, 255, true);
}
});
binding.takeVideo1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takeVideo(1, 255, false);
}
});
mProgressDialog = ProgressDialog.show(this, "", "连接中", true, true);
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
DeviceActivity.this.finish();
}
});
loadDeviceInfo(context);
}
protected void loadDeviceInfo(final Context context) {
final File fileTmp = new File(getFilesDir(), "tmp");
if (!fileTmp.exists()) {
fileTmp.mkdirs();
}
(new Thread(new Runnable() {
@Override
public void run() {
try {
runImpl();
} catch (Exception ex) {
ex.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
showAppInfo();
}
});
}
private void runImpl() {
Dadb adb = Dadb.discover(mDeviceIp, mAdbKeyPair);
if (adb == null) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(DeviceActivity.this, R.string.err_dev_failed_to_connect, Toast.LENGTH_LONG).show();
}
}, 100);
return;
}
boolean res = false;
File appConfigFile = new File(fileTmp, "App.json");
res = pullFile(adb, REMOTE_PATH_DATA + "App.json", appConfigFile);
if (res) {
final MicroPhotoContext.AppConfig appConfig = MicroPhotoContext.getMpAppConfig(context, appConfigFile.getAbsolutePath());
mAppConfig = appConfig;
DeviceActivity.this.mHandler.post(new Runnable() {
@Override
public void run() {
DeviceActivity.this.showAppInfo();
}
});
}
final int numberOfChannels = (mAppConfig != null) ? mAppConfig.channels : 0;
mHandler.post(new Runnable() {
@Override
public void run() {
updateButtons(numberOfChannels);
}
});
mChannelCfgs.clear();
if (mAppConfig != null && mAppConfig.channels > 0) {
for (int channel = 1; channel <= mAppConfig.channels; channel++) {
String remoteFilePath = REMOTE_PATH_DATA_CHANNELS + Integer.toString(channel) + ".json";
File localFilePath = new File(fileTmp, Integer.toString(channel) + ".json");
if (localFilePath.exists()) {
localFilePath.delete();
}
res = pullFile(adb, remoteFilePath, localFilePath);
if (res) {
JSONObject channelJson = JSONUtils.loadJson(localFilePath.getAbsolutePath());
if (channelJson != null) {
mChannelCfgs.put(new Integer(channel), channelJson);
}
localFilePath.delete();
}
}
}
AdbShellResponse adbShellResponse = null;
try {
adbShellResponse = adb.shell("getprop");
} catch (Exception ex) {
ex.printStackTrace();
}
if (adbShellResponse.getExitCode() == 0) {
String[] lines = FileUtils.splitLines(adbShellResponse.getAllOutput());
for (String line : lines) {
int pos = line.indexOf("]: [");
if (pos != -1) {
String key = line.substring(1, pos);
String val = line.substring(pos + 4, line.length() - 1);
mProps.put(key, val);
}
}
}
// adb shell pm dump com.xypower.mpremote | grep "versionName"
adbShellResponse = null;
try {
adbShellResponse = adb.shell("pm dump " + PACKAGE_NAME_MP + " | grep \"versionName\"");
} catch (Exception ex) {
ex.printStackTrace();
}
if (adbShellResponse.getExitCode() == 0) {
String[] lines = FileUtils.splitLines(adbShellResponse.getAllOutput());
for (String line : lines) {
int pos = line.indexOf("versionName=");
if (pos != -1) {
String val = line.substring(pos + 12);
mProps.put(KEY_APP_MP_VERSION, val);
break;
}
}
}
String remoteFilePath = REMOTE_PATH_TMP + "bv.txt";
String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"query_bv\" --es path \"" + remoteFilePath + "\"";
adbShellResponse = null;
try {
adbShellResponse = adb.shell(cmd);
} catch (Exception ex) {
ex.printStackTrace();
}
if (adbShellResponse.getExitCode() == 0) {
File localFilePath = new File(fileTmp, "bv.txt");
if (localFilePath.exists()) {
localFilePath.delete();
}
for (int idx = 0; idx < 10; idx++) {
res = pullFile(adb, remoteFilePath, localFilePath);
if (res) {
String content = FileUtils.readTextFile(localFilePath.getAbsolutePath());
if (!TextUtils.isEmpty(content)) {
int pos = content.indexOf(" ");
if (pos != -1) {
String bv = content.substring(0, pos);
String bcv = content.substring(pos + 1);
if (!TextUtils.isEmpty(bv)) {
mBatteryVoltage = Integer.parseInt(bv);
}
if (!TextUtils.isEmpty(bcv)) {
mBatteryChargingVoltage = Integer.parseInt(bcv);
}
}
}
localFilePath.delete();
break;
}
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
try {
adb.shell("rm " + remoteFilePath);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
})).start();
}
protected boolean pullFile(Dadb adb, String remoteFilePath, File localFilePath) {
if (localFilePath.exists()) {
localFilePath.delete();
}
Log.i(TAG, "PULL: " + remoteFilePath);
boolean res = false;
try {
adb.pull(localFilePath, remoteFilePath);
res = true;
} catch (Exception ex) {
ex.printStackTrace();
}
return res && localFilePath.exists();
}
protected void takePhotoImpl(final String cmd, final int channel, final int preset, final String localFileName, final String remoteFilePath, final boolean photoOrVideo, long sleepTime) {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
Dadb adb = Dadb.discover(mDeviceIp, mAdbKeyPair);
if (adb == null) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(DeviceActivity.this, R.string.err_dev_failed_to_connect, Toast.LENGTH_LONG).show();
}
}, 100);
return;
}
AdbShellResponse adbShellResponse = null;
try {
adbShellResponse = adb.shell(cmd);
} catch (Exception ex) {
ex.printStackTrace();
}
if (adbShellResponse != null) {
if (adbShellResponse.getExitCode() == 0) {
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (Exception ex) {
}
}
File localFilePath = new File(getFilesDir(), localFileName);
for (int idx = 0; idx < 10; idx++) {
boolean res = pullFile(adb, remoteFilePath, localFilePath);
if (res) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (photoOrVideo) {
showPhoto(localFilePath.getAbsolutePath());
} else {
showVideo(localFilePath.getAbsolutePath());
}
}
});
break;
}
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
try {
// mAdb.shell("rm " + remoteFilePath);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
});
th.start();
}
protected long takePhoto(int channel, int preset, final boolean photoOrVideo) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
Date dt = new Date();
int cameraId = channel - 1;
final String fileName = "IMG-" + Integer.toString(channel) + "-" + simpleDateFormat.format(dt) + (photoOrVideo ? ".jpg" : ".mp4");
final String remoteFilePath = REMOTE_PATH_TMP + fileName;
String leftTopOsd = "";
if (mAppConfig != null) {
leftTopOsd += mAppConfig.cmdid + " ";
}
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
leftTopOsd += simpleDateFormat2.format(dt) + " ";
leftTopOsd += "CH:" + Integer.toString(channel) + " ";
leftTopOsd = leftTopOsd.replaceAll("\\\\", "\\\\\\\\");
leftTopOsd = leftTopOsd.replaceAll(" ", "\\\\ ");
String usb = (channel == 4) ? "true" : "false";
JSONObject channelJson = (JSONObject) mChannelCfgs.get(Integer.valueOf(channel));
if (channelJson != null) {
int usbCamera = channelJson.optInt("usbCamera", 0);
usb = (usbCamera != 0) ? "true" : "false";
cameraId = channelJson.optInt("cameraId", cameraId);
}
final String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"take_photo\" --es path \""
+ remoteFilePath + "\" --ez usb " + usb + " --ei cameraId " + Integer.toString(cameraId) + " --ei channel "
+ Integer.toString(channel) + " --ei preset " + Integer.toString(preset) + " --es leftTopOsd \"" + leftTopOsd + "\"";
// adbShellResponse = null;
takePhotoImpl(cmd, channel, preset, fileName, remoteFilePath, photoOrVideo, 0);
return 0;
}
protected long takeVideo(int channel, int preset, final boolean photoOrVideo) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
Date dt = new Date();
int cameraId = channel - 1;
int duration = 5;
String leftTopOsd = "";
if (mAppConfig != null) {
leftTopOsd += mAppConfig.cmdid + " ";
}
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
leftTopOsd += simpleDateFormat2.format(dt) + " ";
leftTopOsd += "CH:" + Integer.toString(channel) + " ";
final String fileName = "IMG-" + Integer.toString(channel) + "-" + simpleDateFormat.format(dt) + (photoOrVideo ? ".jpg" : ".mp4");
final String remoteFilePath = REMOTE_PATH_TMP + fileName;
// int quality = intent.getIntExtra("quality", 0);
// int width = intent.getIntExtra("width", 1280);
// int height = intent.getIntExtra("height", 720);
// int duration = intent.getIntExtra("duration", 15);
// int orientation = intent.getIntExtra("orientation", 0);
// long videoId = System.currentTimeMillis() / 1000;
//
// String leftTopOsd = intent.getStringExtra("leftTopOsd");
// String rightTopOsd = intent.getStringExtra("rightTopOsd");
// String rightBottomOsd = intent.getStringExtra("rightBottomOsd");
// String leftBottomOsd = intent.getStringExtra("leftBottomOsd");
int width = 1280;
int height = 720;
String usb = (channel == 4) ? "true" : "false";
JSONObject channelJson = (JSONObject) mChannelCfgs.get(Integer.valueOf(channel));
if (channelJson != null) {
int usbCamera = channelJson.optInt("usbCamera", 0);
usb = (usbCamera != 0) ? "true" : "false";
cameraId = channelJson.optInt("cameraId", cameraId);
width = channelJson.optInt("videoCX", width);
height = channelJson.optInt("videoCY", height);
}
final String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"recording\" --es path \""
+ remoteFilePath + "\" --ez usb " + usb + " --ei cameraId " + Integer.toString(cameraId) + " --ei channel "
+ Integer.toString(channel) + " --ei preset " + Integer.toString(preset) + " --ei duration " + Integer.toString(duration)
+ " --el videoId " + Long.toString(dt.getTime() / 1000) + " --ei width " + Integer.toString(height) + " --ei height "
+ Integer.toString(height) + " --es leftTopOsd \"" + leftTopOsd + "\"";
// adbShellResponse = null;
Log.d(TAG, cmd);
takePhotoImpl(cmd, channel, preset, fileName, remoteFilePath, photoOrVideo, duration * 1000);
return 0;
}
private void showPhoto(final String filePath) {
Intent intent = new Intent(this, ImageActivity.class);
intent.putExtra("path", filePath);
intent.putExtra("cmdid", TextUtils.isEmpty(mAppConfig.cmdid) ? "" : mAppConfig.cmdid);
// intent.putExtra("info", info);
startActivity(intent);
}
private void showVideo(final String filePath) {
Intent intent = new Intent(DeviceActivity.this, VideoActivity.class);
intent.putExtra("path", filePath);
// intent.putExtra("info", info);
startActivity(intent);
}
private void updateButtons(int numberOfChannels) {
if (numberOfChannels > 0) {
binding.takePhoto1.setVisibility(View.VISIBLE);
binding.takeVideo1.setVisibility(View.VISIBLE);
}
if (numberOfChannels > 1) {
binding.takePhoto2.setVisibility(View.VISIBLE);
binding.takeVideo2.setVisibility(View.VISIBLE);
}
if (numberOfChannels > 2) {
binding.takePhoto3.setVisibility(View.VISIBLE);
binding.takeVideo3.setVisibility(View.VISIBLE);
}
if (numberOfChannels > 3) {
binding.takePhoto4.setVisibility(View.VISIBLE);
binding.takeVideo4.setVisibility(View.VISIBLE);
}
}
public static int calculateSignalLevel(int rsrp, int numLevels) {
int signalLevel = 0;
if (rsrp >= -97) {
signalLevel = 4;
} else if (rsrp >= -107) {
signalLevel = 3;
} else if (rsrp >= -117) {
signalLevel = 2;
} else if (rsrp >= -125) {
signalLevel = 1;
} else if (rsrp >= -140) {
signalLevel = 0;
}
return signalLevel;
}
private void showAppInfo() {
ActionBar actionBar = getSupportActionBar();
// 视频浏览,拍照,设备信息及状态查询(编号版本双SIM卡状态电池及太阳能电压等),可以参考东视
Resources resources = getResources();
// String buildTime = BuildConfig.BUILD_
// Date date = new Date(BuildConfig.BUILD_TIMESTAMP);
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String title = resources.getString(R.string.app_name);
if (!TextUtils.isEmpty(mAppConfig.cmdid)) {
title = mAppConfig.cmdid;
}
actionBar.setTitle(title);
StringBuilder stringBuilder = new StringBuilder();
ConnectivityManager mConnectivityManager;
if (mProps.containsKey(KEY_RO_SERIALNO)) {
String val = (String)mProps.get(KEY_RO_SERIALNO);
appendKeyValue(stringBuilder, "序列号", val, true);
}
int numberOfSimcard = 0;
if (mProps.containsKey(KEY_RO_MODEMS_MAX_COUNT)) {
String val = (String)mProps.get(KEY_RO_MODEMS_MAX_COUNT);
numberOfSimcard = Integer.parseInt(val);
}
String signalLevel = "";
for (int idx = 1; idx <= numberOfSimcard; idx++) {
String key = KEY_RO_SIGNAL_STRNGTH_PREFIX + Integer.toString(idx);
if (mProps.containsKey(key)) {
String val = (String)mProps.get(key);
int pos = val.indexOf(",");
if (pos != -1) {
int signalStrength = Integer.parseInt(val.substring(0, pos));
if (!TextUtils.isEmpty(signalLevel)) {
signalLevel += "/";
}
TelephonyManager telephonyManager;
SignalStrength signalStrength1;
int ss = calculateSignalLevel(signalStrength, RSSI_LEVELS);
signalLevel += Integer.toString(ss);
}
}
}
appendKeyValue(stringBuilder, "CMDID", mAppConfig.cmdid, true);
appendKeyValue(stringBuilder, "主站", mAppConfig.server + ":" + Integer.toString(mAppConfig.port));
appendKeyValue(stringBuilder, "通道数", Integer.toString(mAppConfig.channels));
if (mProps.containsKey(KEY_APP_MP_VERSION)) {
String val = (String)mProps.get(KEY_APP_MP_VERSION);
appendKeyValue(stringBuilder, "APP版本", val);
}
String vendorName = "";
if (mProps.containsKey(KEY_GSM_VENDOR_NAME)) {
vendorName = mProps.get(KEY_GSM_VENDOR_NAME);
}
appendKeyValue(stringBuilder, "移动运营商", vendorName);
appendKeyValue(stringBuilder, "移动信号", signalLevel);
if (mBatteryVoltage != -1) {
appendKeyValue(stringBuilder, "电池电压", Integer.toString(mBatteryVoltage / 1000) + "." + Integer.toString((mBatteryVoltage % 1000) / 100));
}
if (mBatteryChargingVoltage != -1) {
appendKeyValue(stringBuilder, "充电电压", Integer.toString(mBatteryChargingVoltage / 1000) + "." + Integer.toString((mBatteryChargingVoltage % 1000) / 100));
}
binding.deviceInfo.setText(Html.fromHtml(stringBuilder.toString()));
}
private void appendKeyValue(StringBuilder stringBuilder, String key, String val) {
appendKeyValue(stringBuilder, key, val, false);
}
private void appendKeyValue(StringBuilder stringBuilder, String key, String val, boolean highlignt) {
if (highlignt) {
stringBuilder.append(key + "<font color=\"red\"><b>" + val + "</b></font>");
} else {
stringBuilder.append(key + "" + val);
}
stringBuilder.append("<br>");
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return false;
default:
return super.onOptionsItemSelected(item);
}
}
}