diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f273bcc..17fa286 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,8 @@ + + + + /> \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpremote/DeviceActivity.java b/app/src/main/java/com/xypower/mpremote/DeviceActivity.java new file mode 100644 index 0000000..bcd15a5 --- /dev/null +++ b/app/src/main/java/com/xypower/mpremote/DeviceActivity.java @@ -0,0 +1,516 @@ +package com.xypower.mpremote; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +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.MicroPhotoContext; +import com.xypower.mpremote.databinding.ActivityDeviceBinding; +import com.xypower.mpremote.databinding.ActivityMainBinding; + +import java.io.File; +import java.net.Socket; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +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; + + 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_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_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 Handler mHandler; + private Dadb mAdb; + + private Map mProps = 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); + + 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); + } + + final File fileTmp = new File(getFilesDir(), "tmp"); + if (!fileTmp.exists()) { + fileTmp.mkdirs(); + } + + final AdbKeyPair adbKeyPair = AdbKeyPair.read(priKeyFile, pubKeyFile); + final Context context = getApplicationContext(); + + (new Thread(new Runnable() { + @Override + public void run() { + + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + + String ipAddressByWifi = null; + if (wifiManager != null) { + ipAddressByWifi = Formatter.formatIpAddress(wifiManager.getDhcpInfo().ipAddress); + } + + if (ipAddressByWifi.contains(WIFI_IP_PREFIX)) { + + + String deviceIP = Formatter.formatIpAddress(wifiManager.getDhcpInfo().gateway); + if (DEBUG) { + deviceIP = WIFI_IP_DEVICE; + } + + Socket mSocket = null; + try { + mSocket = new Socket(deviceIP, ADB_SERVER_PORT); + if (mSocket.isConnected()) { + mSocket.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + Dadb adb = Dadb.discover(deviceIP, adbKeyPair); + + if (adb != null) { + + mAdb = adb; + + AdbShellResponse adbShellResponse = null; + try { + adbShellResponse = mAdb.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 = mAdb.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); + } + } + } + + 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 = mAdb.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++) { + + boolean res = pullFile(mAdb, 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 { + mAdb.shell("rm " + remoteFilePath); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + File appConfigFile = new File(fileTmp, "App.json"); + boolean 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(); + } + }); + } + + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(DeviceActivity.this, R.string.err_dev_not_found, Toast.LENGTH_LONG).show(); + } + }); + } + + } + + // Dadb dadb = Dadb.create("localhost", 5555, adbKeyPair); + // Dadb.discover(); + + + + } + })).start(); + + 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); + } + }); + + } + + + 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() { + if (sleepTime > 0) { + try { + Thread.sleep(sleepTime); + } catch (Exception ex) { + } + } + + AdbShellResponse adbShellResponse = null; + try { + adbShellResponse = mAdb.shell(cmd); + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (adbShellResponse != null) { + if (adbShellResponse.getExitCode() == 0) { + + File localFilePath = new File(getFilesDir(), localFileName); + + for (int idx = 0; idx < 10; idx++) { + + boolean res = pullFile(mAdb, 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) { + if (mAdb == null) { + return 0; + } + + 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) + " "; + + String usb = (channel == 4) ? "true" : "false"; + 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) { + if (mAdb == null) { + return 0; + } + + 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"); + + String usb = (channel == 4) ? "true" : "false"; + 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) + " --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(DeviceActivity.this, ImageActivity.class); + intent.putExtra("path", filePath); + // 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 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(); + + if (mProps.containsKey(KEY_RO_SERIALNO)) { + String val = (String)mProps.get(KEY_RO_SERIALNO); + appendKeyValue(stringBuilder, "序列号", val); + } + + appendKeyValue(stringBuilder, "CMDID", mAppConfig.cmdid); + + 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); + } + + 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) { + stringBuilder.append(key + ":" + val + ""); + stringBuilder.append("
"); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return false; + default: + return super.onOptionsItemSelected(item); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpremote/ImageActivity.java b/app/src/main/java/com/xypower/mpremote/ImageActivity.java index 537ce24..149dfe5 100644 --- a/app/src/main/java/com/xypower/mpremote/ImageActivity.java +++ b/app/src/main/java/com/xypower/mpremote/ImageActivity.java @@ -6,6 +6,7 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; @@ -80,22 +81,12 @@ public class ImageActivity extends AppCompatActivity { binding = ActivityImageBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - binding.imageViewOpt.setImageDrawable(null); - binding.imageViewAE.setImageDrawable(null); + binding.imageView.setImageDrawable(null); - binding.imageViewOpt.setVisibility(View.GONE); + binding.imageView.setClickable(true); + binding.imageView.setOnTouchListener(mDelayHideTouchListener); - binding.imageViewAE.setClickable(true); - binding.imageViewAE.setOnTouchListener(mDelayHideTouchListener); - - binding.imageViewAE.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ImageActivity.this.finish(); - } - }); - - binding.imageViewOpt.setOnClickListener(new View.OnClickListener() { + binding.imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ImageActivity.this.finish(); @@ -110,6 +101,13 @@ public class ImageActivity extends AppCompatActivity { } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + binding.imageView.requestLayout(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -123,32 +121,18 @@ public class ImageActivity extends AppCompatActivity { private boolean loadImage(String path) { - binding.imageViewOpt.setVisibility(View.GONE); - binding.imageViewAE.setVisibility(View.GONE); + binding.imageView.setImageDrawable(null); File file = new File(path); if (!file.exists()) { return false; } Drawable drawable = loadDrawable(path); - Drawable drawableAE = loadDrawable(path + ".ae.jpg"); - if (drawableAE == null && drawable == null) { - return false; - } - if (drawableAE != null && drawable != null) { - binding.imageViewOpt.setImageDrawable(drawable); - binding.imageViewAE.setImageDrawable(drawableAE); - binding.imageViewOpt.setVisibility(View.VISIBLE); - binding.imageViewAE.setVisibility(View.VISIBLE); - } - else if (drawable != null) { - binding.imageViewOpt.setImageDrawable(drawable); - binding.imageViewAE.setImageDrawable(null); - binding.imageViewOpt.setVisibility(View.VISIBLE); - // binding.imageViewAE.setVisibility(View.VISIBLE); + if (drawable != null) { + binding.imageView.setImageDrawable(drawable); } - return true; + return drawable != null; } private Drawable loadDrawable(String file) { diff --git a/app/src/main/java/com/xypower/mpremote/MainActivity.java b/app/src/main/java/com/xypower/mpremote/MainActivity.java index 6d3e799..4da4d8d 100644 --- a/app/src/main/java/com/xypower/mpremote/MainActivity.java +++ b/app/src/main/java/com/xypower/mpremote/MainActivity.java @@ -13,6 +13,7 @@ import android.text.Html; import android.text.TextUtils; import android.text.format.Formatter; import android.util.Log; +import android.view.Menu; import android.view.View; import android.widget.Toast; @@ -279,6 +280,14 @@ public class MainActivity extends AppCompatActivity { } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main_activity_actions, menu); + return true; + } + + protected boolean pullFile(Dadb adb, String remoteFilePath, File localFilePath) { if (localFilePath.exists()) { localFilePath.delete(); diff --git a/app/src/main/java/com/xypower/mpremote/VideoActivity.java b/app/src/main/java/com/xypower/mpremote/VideoActivity.java index e7d5ac8..f7ead08 100644 --- a/app/src/main/java/com/xypower/mpremote/VideoActivity.java +++ b/app/src/main/java/com/xypower/mpremote/VideoActivity.java @@ -4,6 +4,7 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.view.MenuItem; import android.widget.MediaController; @@ -26,7 +27,6 @@ public class VideoActivity extends AppCompatActivity { if (path != null) { VideoView videoView = (VideoView)findViewById(R.id.videoView); - videoView.setVideoPath(path); //创建MediaController对象 @@ -42,6 +42,13 @@ public class VideoActivity extends AppCompatActivity { } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + VideoView videoView = (VideoView)findViewById(R.id.videoView); + videoView.requestLayout(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { diff --git a/app/src/main/res/drawable/ic_action_refresh.xml b/app/src/main/res/drawable/ic_action_refresh.xml new file mode 100644 index 0000000..788bfdf --- /dev/null +++ b/app/src/main/res/drawable/ic_action_refresh.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_device.xml b/app/src/main/res/layout/activity_device.xml new file mode 100644 index 0000000..9bf68d4 --- /dev/null +++ b/app/src/main/res/layout/activity_device.xml @@ -0,0 +1,42 @@ + + + + + +