diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dc25f814..f32efefc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -83,19 +83,23 @@
android:supportsRtl="true"
android:theme="@style/Theme.MicroPhoto"
tools:targetApi="28">
+
+
+ android:exported="true" />
-
+ android:process=":bridge_proc"
+ android:screenOrientation="landscape" />
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/xypower/mpapp/BridgeProvider.java b/app/src/main/java/com/xypower/mpapp/BridgeProvider.java
new file mode 100644
index 00000000..27a0e532
--- /dev/null
+++ b/app/src/main/java/com/xypower/mpapp/BridgeProvider.java
@@ -0,0 +1,344 @@
+package com.xypower.mpapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.xypower.common.FilesUtils;
+import com.xypower.common.JSONUtils;
+import com.xypower.common.MicroPhotoContext;
+
+import org.json.JSONObject;
+
+import java.io.File;
+
+public class BridgeProvider extends ContentProvider {
+
+ private final static String TAG = "MPLOG";
+ private final static String AUTHORITY = "com.xypower.mpapp.provider";
+ private final static String PATH_QUERY_SEC_VERSION = "/querySecVersion";
+ private final static String PATH_QUERY_BATTERY_VOLTAGE = "/queryBatVol";
+ private final static String PATH_IMP_PRI_KEY = "/importPriKey";
+ private final static String PATH_IMP_PUB_KEY = "/importPubKey";
+ private final static String PATH_GEN_KEYS = "/genKeys";
+ private final static String PATH_GEN_CERT_REQ = "/genCertReq";
+
+ private final static String PATH_TAKE_PHOTO = "/takePhoto";
+ private final static String PATH_TAKE_VIDEO = "/takeVideo";
+
+ public BridgeProvider() {
+ Log.i(TAG, "BridgeProvider");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // Implement this to handle requests to delete one or more rows.
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ // TODO: Implement this to handle requests for the MIME type of the data
+ // at the given URI.
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+
+
+ return null;
+ }
+
+ @Override
+ public boolean onCreate() {
+ // TODO: Implement this to initialize your content provider on startup.
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ Log.i(TAG, uri.toString());
+ matcher.addURI(AUTHORITY, PATH_QUERY_SEC_VERSION, 1);
+ matcher.addURI(AUTHORITY, PATH_QUERY_BATTERY_VOLTAGE, 2);
+
+
+ Cursor cursor = null;
+ int matched = matcher.match(uri);
+ switch (matched) {
+ case 1:
+ cursor = querySecVersion();
+ break;
+ case 2:
+ cursor = queryBattaryVoltage();
+ break;
+ default:
+ break;
+ }
+
+ return cursor;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ matcher.addURI(AUTHORITY, PATH_IMP_PRI_KEY, 1);
+ matcher.addURI(AUTHORITY, PATH_IMP_PUB_KEY, 2);
+ matcher.addURI(AUTHORITY, PATH_GEN_KEYS, 3);
+ matcher.addURI(AUTHORITY, PATH_GEN_CERT_REQ, 4);
+ matcher.addURI(AUTHORITY, PATH_TAKE_PHOTO, 5);
+ matcher.addURI(AUTHORITY, PATH_TAKE_VIDEO, 6);
+
+ int res = 0;
+ int matched = matcher.match(uri);
+ switch (matched) {
+ case 1:
+ res = importPrivateKey(uri, values);
+ break;
+ case 2:
+ res = importPublicKey(uri, values);
+ break;
+ case 3:
+ res = genKeys(uri, values);
+ break;
+ case 4:
+ res = genCertReq(uri, values);
+ break;
+ case 5:
+ res = takePhoto(uri, values);
+ break;
+ case 6:
+ res = takeVideo(uri, values);
+ break;
+ default:
+ break;
+ }
+
+ return res;
+ }
+
+ private Cursor querySecVersion() {
+ // String resultFile = intent.getStringExtra("resultFile");
+ String version = MicroPhotoService.querySecVersion();
+ String[] columns = { "version" };
+ MatrixCursor matrixCursor = new MatrixCursor(columns, 1);
+ matrixCursor.addRow(new Object[] { version });
+ return matrixCursor;
+ }
+
+ private Cursor queryBattaryVoltage() {
+ // #define CMD_GET_CHARGING_BUS_VOLTAGE_STATE 112
+ // #define CMD_GET_BAT_VOL_STATE 115
+ // #define CMD_GET_BAT_BUS_VOLTAGE_STATE 117
+ int bv = MicroPhotoService.getGpioInt(117);
+ int bcv = MicroPhotoService.getGpioInt(112);
+ String[] columns = { "bv", "bcv" };
+ MatrixCursor matrixCursor = new MatrixCursor(columns, 1);
+ matrixCursor.addRow(new Object[] { Integer.valueOf(bv), Integer.valueOf(bcv) });
+ return matrixCursor;
+ }
+
+
+ private int importPrivateKey(Uri uri, ContentValues values) {
+ String cert = values.containsKey("cert") ? values.getAsString("cert") : null;
+ String path = values.containsKey("path") ? values.getAsString("path") : null;
+ String resultFile = values.containsKey("resultFile") ? values.getAsString("path") : null;
+ int index = values.containsKey("index") ? values.getAsInteger("index").intValue() : 0;
+
+ boolean res = false;
+ if (!TextUtils.isEmpty(cert)) {
+ // Import
+ byte[] content = Base64.decode(cert, Base64.DEFAULT);
+ if (content != null) {
+ res = MicroPhotoService.importPrivateKey(index, content);
+ }
+ } else if (TextUtils.isEmpty(path)) {
+ String md5 = values.containsKey("md5") ? values.getAsString("md5") : null;
+ File file = new File(path);
+ if (file.exists() && file.isFile()) {
+ res = MicroPhotoService.importPrivateKeyFile(index, path, md5);
+ }
+ }
+
+ return res ? 1 : 0;
+ /*
+ Log.i(TAG, "Import Private Key result=" + (res ? "1" : "0"));
+
+ if (!TextUtils.isEmpty(resultFile)) {
+ FilesUtils.ensureParentDirectoryExisted(resultFile);
+ FilesUtils.writeTextFile(resultFile, res ? "1" : "0");
+ }
+ */
+ }
+
+ private int importPublicKey(Uri uri, ContentValues values) {
+ String cert = values.containsKey("cert") ? values.getAsString("cert") : null;
+ String path = values.containsKey("path") ? values.getAsString("path") : null;
+ String resultFile = values.containsKey("resultFile") ? values.getAsString("path") : null;
+ int index = values.containsKey("index") ? values.getAsInteger("index").intValue() : 0;
+
+ boolean res = false;
+ if (!TextUtils.isEmpty(cert)) {
+ // Import
+ byte[] content = Base64.decode(cert, Base64.DEFAULT);
+ if (content != null) {
+ res = MicroPhotoService.importPublicKey(index, content);
+ }
+ } else if (TextUtils.isEmpty(path)) {
+ String md5 = values.containsKey("md5") ? values.getAsString("md5") : null;
+ File file = new File(path);
+ if (file.exists() && file.isFile()) {
+ res = MicroPhotoService.importPublicKeyFile(index, path, md5);
+ }
+ }
+
+ return res ? 1 : 0;
+ /*
+ Log.i(TAG, "Import Private Key result=" + (res ? "1" : "0"));
+
+ if (!TextUtils.isEmpty(resultFile)) {
+ FilesUtils.ensureParentDirectoryExisted(resultFile);
+ FilesUtils.writeTextFile(resultFile, res ? "1" : "0");
+ }
+ */
+ }
+
+ private int genKeys(Uri uri, ContentValues values) {
+ int index = values.containsKey("index") ? values.getAsInteger("index").intValue() : 0;
+ boolean res = MicroPhotoService.genKeys(index);
+ return res ? 1 : 0;
+ }
+
+ private int genCertReq(Uri uri, ContentValues values) {
+ int index = values.containsKey("index") ? values.getAsInteger("index").intValue() : 0;
+ int type = values.containsKey("type") ? values.getAsInteger("type").intValue() : 0;
+ String subject = values.containsKey("subject") ? values.getAsString("subject") : null;
+ String path = values.containsKey("path") ? values.getAsString("path") : null;
+
+ if (TextUtils.isEmpty(subject) || TextUtils.isEmpty(path)) {
+ return 0;
+ }
+
+ boolean res = MicroPhotoService.genCertRequest(index, type, subject, path);
+ return res ? 1 : 0;
+ }
+
+ private int takePhoto(Uri uri, ContentValues values) {
+ String path = values.containsKey("path") ? values.getAsString("path") : null;
+ int channel = values.containsKey("channel") ? values.getAsInteger("channel").intValue() : 1;
+ int preset = values.containsKey("preset") ? values.getAsInteger("preset").intValue() : 0xFF;
+
+ int width = values.containsKey("width") ? values.getAsInteger("width").intValue() : 0;
+ int height = values.containsKey("height") ? values.getAsInteger("height").intValue() : 0;
+
+ String leftTopOsd = values.containsKey("leftTopOsd") ? values.getAsString("leftTopOsd") : null;
+ String rightTopOsd = values.containsKey("rightTopOsd") ? values.getAsString("rightTopOsd") : null;
+ String rightBottomOsd = values.containsKey("rightBottomOsd") ? values.getAsString("rightBottomOsd") : null;
+ String leftBottomOsd = values.containsKey("leftBottomOsd") ? values.getAsString("leftBottomOsd") : null;
+
+ String appPath = MicroPhotoContext.buildMpAppDir(getContext());
+ File configFile = new File(appPath);
+ configFile = new File(configFile, "data/channels/" + Integer.toString(channel) + ".json");
+
+ File tmpConfigFile = new File(appPath);
+ tmpConfigFile = new File(tmpConfigFile, "tmp/" + Integer.toString(channel) + "-" + Long.toString(System.currentTimeMillis()) + ".json");
+
+ if (configFile.exists()) {
+ try {
+ FilesUtils.copyFile(configFile, tmpConfigFile);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ JSONObject configJson = JSONUtils.loadJson(tmpConfigFile.getAbsolutePath());
+ try {
+ if (configJson == null) {
+ configJson = new JSONObject();
+ }
+ if (width > 0) {
+ configJson.put("resolutionCX", width);
+ }
+ if (height > 0) {
+ configJson.put("resolutionCY", height);
+ }
+
+ JSONObject osdJson = configJson.getJSONObject("osd");
+ if (osdJson == null) {
+ osdJson = configJson.put("osd", new JSONObject());
+ }
+ osdJson.put("leftTop", TextUtils.isEmpty(leftTopOsd) ? "" : leftTopOsd);
+ osdJson.put("rightTop", TextUtils.isEmpty(rightTopOsd) ? "" : rightTopOsd);
+ osdJson.put("rightBottom", TextUtils.isEmpty(rightBottomOsd) ? "" : rightBottomOsd);
+ osdJson.put("leftBottom", TextUtils.isEmpty(leftBottomOsd) ? "" : leftBottomOsd);
+
+ JSONUtils.saveJson(tmpConfigFile.getAbsolutePath(), configJson);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ File file = new File(path);
+ if (file.exists()) {
+ file.delete();
+ } else {
+ FilesUtils.ensureParentDirectoryExisted(path);
+ }
+
+ MicroPhotoService.takePhoto(channel, preset, true, tmpConfigFile.getAbsolutePath(), path);
+ if (tmpConfigFile.exists()) {
+ tmpConfigFile.delete();
+ }
+
+ return 1;
+ }
+
+ private int takeVideo(Uri uri, ContentValues values) {
+ String path = values.containsKey("path") ? values.getAsString("path") : null;
+ int channel = values.containsKey("channel") ? values.getAsInteger("channel").intValue() : 1;
+ int preset = values.containsKey("preset") ? values.getAsInteger("preset").intValue() : 0xFF;
+
+ int width = values.containsKey("width") ? values.getAsInteger("width").intValue() : 0;
+ int height = values.containsKey("height") ? values.getAsInteger("height").intValue() : 0;
+
+ int quality = values.containsKey("quality") ? values.getAsInteger("quality").intValue() : 0;
+ int cameraId = values.containsKey("cameraId") ? values.getAsInteger("cameraId").intValue() : -1;
+ int duration = values.containsKey("duration") ? values.getAsInteger("duration").intValue() : 15;
+
+ int orientation = values.containsKey("orientation") ? values.getAsInteger("orientation").intValue() : 0;
+
+ long videoId = System.currentTimeMillis() / 1000;
+
+ String leftTopOsd = values.containsKey("leftTopOsd") ? values.getAsString("leftTopOsd") : null;
+ String rightTopOsd = values.containsKey("rightTopOsd") ? values.getAsString("rightTopOsd") : null;
+ String rightBottomOsd = values.containsKey("rightBottomOsd") ? values.getAsString("rightBottomOsd") : null;
+ String leftBottomOsd = values.containsKey("leftBottomOsd") ? values.getAsString("leftBottomOsd") : null;
+
+ if (cameraId == -1) {
+ cameraId = channel - 1;
+ }
+
+ Context context = getContext();
+
+ Intent recordingIntent = MicroPhotoService.makeRecordingIntent(context,
+ cameraId, videoId, duration, width, height, quality, orientation,
+ leftTopOsd, rightTopOsd, rightBottomOsd, leftBottomOsd);
+
+ recordingIntent.putExtra("ActivityResult", false);
+ context.startActivity(recordingIntent);
+
+ return 1;
+ }
+}
\ No newline at end of file