diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp index dea570fa..2853725e 100644 --- a/app/src/main/cpp/MicroPhoto.cpp +++ b/app/src/main/cpp/MicroPhoto.cpp @@ -223,7 +223,7 @@ Java_com_xypower_mpapp_MicroPhotoService_init( extern "C" JNIEXPORT jboolean JNICALL Java_com_xypower_mpapp_MicroPhotoService_notifyToTakePhoto( JNIEnv* env, - jobject pThis, jlong handler, jint channel, jint preset, jlong scheduleTime, jstring path, jstring fileName, jboolean sendToCma) { + jobject pThis, jlong handler, jint channel, jint preset, jlong scheduleTime, jboolean sendToCma) { if (channel < 1 || channel > 0xFF) { diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index 3411f3c9..a933095e 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -21,6 +21,7 @@ #include "PhoneDevice.h" #include #include +#include #include "ncnn/yolov5ncnn.h" #include @@ -242,7 +243,15 @@ void CPhoneDevice::SetRecognizationCfg(const IDevice::CFG_RECOGNIZATION* pRecogn ncnn_init(); std::string paramFile = m_appPath + (APP_PATH_RECOG_PARAM); std::string binFile = m_appPath + (APP_PATH_RECOG_BIN); - YoloV5Ncnn_Init(paramFile, binFile); + bool res = YoloV5Ncnn_Init(paramFile, binFile); + if (res) + { + XYLOG(XYLOG_SEVERITY_INFO, "Succeeded to Init NCNN"); + } + else + { + XYLOG(XYLOG_SEVERITY_ERROR, "Failed to Init NCNN"); + } } m_pRecognizationCfg = pRecognizationCfg; @@ -738,11 +747,11 @@ bool CPhoneDevice::OnImageReady(cv::Mat& mat) // cv::Rect rc(0, 0, mat.cols, mat.rows); // cv::rectangle (mat, rc, cv::Scalar(255, 255, 255), cv::FILLED); + std::vector objs; if ((m_pRecognizationCfg != NULL) && (m_pRecognizationCfg->enabled != 0) && (mPhotoInfo.recognization != 0)) { // visualize(ncnnPath.c_str(), in); - std::vector objs; #ifdef _DEBUG double startTime = ncnn::get_current_time(); #endif // _DEBUG @@ -771,7 +780,7 @@ bool CPhoneDevice::OnImageReady(cv::Mat& mat) }; #endif cv::Scalar borderColor(m_pRecognizationCfg->borderColor & 0xFF, (m_pRecognizationCfg->borderColor & 0xFF00) >> 8, (m_pRecognizationCfg->borderColor & 0xFF0000) >> 16); - for (std::vector::const_iterator it = objs.cbegin(); it != objs.cend(); ++it) + for (std::vector::const_iterator it = objs.cbegin(); it != objs.cend(); ++it) { if (it->label >= m_pRecognizationCfg->items.size()) { @@ -860,7 +869,7 @@ bool CPhoneDevice::OnImageReady(cv::Mat& mat) { ALOGI("Succeeded to write photo: %s", fullPath.c_str()); } - TakePhotoCb(res, mPhotoInfo, fullPath, time(NULL)); + TakePhotoCb(res, mPhotoInfo, fullPath, time(NULL), objs); } else { diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index 7bc42b4d..083874fc 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -193,11 +193,22 @@ protected: bool SendBroadcastMessage(std::string action, int value); // bool MatchCaptureSizeRequest(ACameraManager *cameraManager, const char *selectedCameraId, unsigned int width, unsigned int height, uint32_t cameraOrientation_, + inline bool TakePhotoCb(bool res, const IDevice::PHOTO_INFO& photoInfo, const string& path, time_t photoTime, const std::vector& objects) const + { + if (m_listener != NULL) + { + return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime, objects); + } + + return false; + } + inline bool TakePhotoCb(bool res, const IDevice::PHOTO_INFO& photoInfo, const string& path, time_t photoTime) const { if (m_listener != NULL) { - return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime); + std::vector objects; + return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime, objects); } return false; diff --git a/app/src/main/cpp/PhoneDevice2.h b/app/src/main/cpp/PhoneDevice2.h index dc9cfe43..6daffbc1 100644 --- a/app/src/main/cpp/PhoneDevice2.h +++ b/app/src/main/cpp/PhoneDevice2.h @@ -67,7 +67,8 @@ protected: { if (m_listener != NULL) { - return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime); + std::vector objects; + return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime, objects); } return false; diff --git a/app/src/main/cpp/ncnn/yolov5ncnn.cpp b/app/src/main/cpp/ncnn/yolov5ncnn.cpp index 75882087..ca06cb05 100644 --- a/app/src/main/cpp/ncnn/yolov5ncnn.cpp +++ b/app/src/main/cpp/ncnn/yolov5ncnn.cpp @@ -9,7 +9,7 @@ ncnn::Net yolov5; DEFINE_LAYER_CREATOR(YoloV5Focus) -void qsort_descent_inplace(std::vector& faceobjects, int left, int right) +void qsort_descent_inplace(std::vector& faceobjects, int left, int right) { int i = left; int j = right; @@ -46,7 +46,7 @@ void qsort_descent_inplace(std::vector& faceobjects, int left, int right } } -void qsort_descent_inplace(std::vector& faceobjects) +void qsort_descent_inplace(std::vector& faceobjects) { if (faceobjects.empty()) return; @@ -54,7 +54,7 @@ void qsort_descent_inplace(std::vector& faceobjects) qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1); } -void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold) +void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold) { picked.clear(); @@ -68,12 +68,12 @@ void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& for (int i = 0; i < n; i++) { - const Object& a = faceobjects[i]; + const IDevice::RECOG_OBJECT& a = faceobjects[i]; int keep = 1; for (int j = 0; j < (int)picked.size(); j++) { - const Object& b = faceobjects[picked[j]]; + const IDevice::RECOG_OBJECT& b = faceobjects[picked[j]]; // intersection over union float inter_area = intersection_area(a, b); @@ -88,7 +88,7 @@ void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& } } -void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector& objects) +void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector& objects) { const int num_grid = feat_blob.h; @@ -162,7 +162,7 @@ void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& i float x1 = pb_cx + pb_w * 0.5f; float y1 = pb_cy + pb_h * 0.5f; - Object obj; + IDevice::RECOG_OBJECT obj; obj.x = x0; obj.y = y0; obj.w = x1 - x0; @@ -177,6 +177,7 @@ void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& i } } + // public native boolean Init(AssetManager mgr); bool YoloV5Ncnn_Init(const std::string& paramFile, const std::string& binFile) { @@ -221,7 +222,7 @@ bool YoloV5Ncnn_Init(const std::string& paramFile, const std::string& binFile) } // public native Obj[] Detect(Bitmap bitmap, boolean use_gpu); -bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& objects) +bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& objects) { if (use_gpu && ncnn::get_gpu_count() == 0) { @@ -282,7 +283,7 @@ bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& object ex.input("images", in_pad); - std::vector proposals; + std::vector proposals; // anchor setting from yolov5/models/yolov5s.yaml @@ -299,7 +300,7 @@ bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& object anchors[4] = 33.f; anchors[5] = 23.f; - std::vector objects8; + std::vector objects8; generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8); proposals.insert(proposals.end(), objects8.begin(), objects8.end()); @@ -318,7 +319,7 @@ bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& object anchors[4] = 59.f; anchors[5] = 119.f; - std::vector objects16; + std::vector objects16; generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16); proposals.insert(proposals.end(), objects16.begin(), objects16.end()); @@ -337,7 +338,7 @@ bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& object anchors[4] = 373.f; anchors[5] = 326.f; - std::vector objects32; + std::vector objects32; generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32); proposals.insert(proposals.end(), objects32.begin(), objects32.end()); @@ -413,7 +414,7 @@ bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& object return true; } -bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) +bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) { if (use_gpu && ncnn::get_gpu_count() == 0) { @@ -472,7 +473,7 @@ bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) ex.input("images", in_pad); - std::vector proposals; + std::vector proposals; // anchor setting from yolov5/models/yolov5s.yaml @@ -489,7 +490,7 @@ bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) anchors[4] = 33.f; anchors[5] = 23.f; - std::vector objects8; + std::vector objects8; generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8); proposals.insert(proposals.end(), objects8.begin(), objects8.end()); @@ -508,7 +509,7 @@ bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) anchors[4] = 59.f; anchors[5] = 119.f; - std::vector objects16; + std::vector objects16; generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16); proposals.insert(proposals.end(), objects16.begin(), objects16.end()); @@ -527,7 +528,7 @@ bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects) anchors[4] = 373.f; anchors[5] = 326.f; - std::vector objects32; + std::vector objects32; generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32); proposals.insert(proposals.end(), objects32.begin(), objects32.end()); diff --git a/app/src/main/cpp/ncnn/yolov5ncnn.h b/app/src/main/cpp/ncnn/yolov5ncnn.h index bc9167fc..7fa27d86 100644 --- a/app/src/main/cpp/ncnn/yolov5ncnn.h +++ b/app/src/main/cpp/ncnn/yolov5ncnn.h @@ -14,6 +14,8 @@ #include +#include + extern ncnn::UnlockedPoolAllocator g_blob_pool_allocator; extern ncnn::PoolAllocator g_workspace_pool_allocator; @@ -65,17 +67,7 @@ public: } }; -struct Object -{ - float x; - float y; - float w; - float h; - int label; - float prob; -}; - -static inline float intersection_area(const Object& a, const Object& b) +static inline float intersection_area(const IDevice::RECOG_OBJECT& a, const IDevice::RECOG_OBJECT& b) { if (a.x > b.x + b.w || a.x + a.w < b.x || a.y > b.y + b.h || a.y + a.h < b.y) { @@ -89,18 +81,18 @@ static inline float intersection_area(const Object& a, const Object& b) return inter_width * inter_height; } -void qsort_descent_inplace(std::vector& faceobjects, int left, int right); +void qsort_descent_inplace(std::vector& faceobjects, int left, int right); -void qsort_descent_inplace(std::vector& faceobjects); +void qsort_descent_inplace(std::vector& faceobjects); -void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold); +void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold); static inline float sigmoid(float x) { return static_cast(1.f / (1.f + exp(-x))); } -void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector& objects); +void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector& objects); inline void ncnn_init() { @@ -116,5 +108,5 @@ inline void ncnn_uninit() bool YoloV5Ncnn_Init(const std::string& paramFile, const std::string& binFile); // public native Obj[] Detect(Bitmap bitmap, boolean use_gpu); -bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& objects); -bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects); \ No newline at end of file +bool YoloV5NcnnDetect( ncnn::Mat& mat, bool use_gpu, std::vector& objects); +bool YoloV5NcnnDetect( cv::Mat& mat, bool use_gpu, std::vector& objects); \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpapp/AppMaster.java b/app/src/main/java/com/xypower/mpapp/AppMaster.java index 7c975b4a..fba9bed5 100644 --- a/app/src/main/java/com/xypower/mpapp/AppMaster.java +++ b/app/src/main/java/com/xypower/mpapp/AppMaster.java @@ -20,6 +20,7 @@ import android.util.Pair; import com.dev.devapi.api.SysApi; import com.xypower.common.FileDownloader; +import com.xypower.common.MicroPhotoContext; import org.json.JSONObject; @@ -119,7 +120,7 @@ public class AppMaster { private void upgradeApp(String url) { FileDownloader dl = new FileDownloader(); - File path = new File(MicroPhotoService.buildAppDir(mService.getApplicationContext()), "packages"); + File path = new File(MicroPhotoContext.buildAppDir(mService.getApplicationContext()), "packages"); if (!path.exists()) { path.mkdirs(); } diff --git a/app/src/main/java/com/xypower/mpapp/ChannelActivity.java b/app/src/main/java/com/xypower/mpapp/ChannelActivity.java index 99469c87..f013be84 100644 --- a/app/src/main/java/com/xypower/mpapp/ChannelActivity.java +++ b/app/src/main/java/com/xypower/mpapp/ChannelActivity.java @@ -8,8 +8,8 @@ import android.text.TextUtils; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; -import androidx.appcompat.app.ActionBar; +import com.xypower.common.MicroPhotoContext; import com.xypower.mpapp.databinding.ActivityChannelBinding; import org.json.JSONException; @@ -99,7 +99,7 @@ public class ChannelActivity extends AppCompatActivity { binding.exposuretime.setText("0"); binding.sensitivity.setText("0"); - String appPath = MicroPhotoService.buildAppDir(getApplicationContext()); + String appPath = MicroPhotoContext.buildAppDir(getApplicationContext()); InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; @@ -169,7 +169,7 @@ public class ChannelActivity extends AppCompatActivity { private void saveChannelParams(int channel) { JSONObject jsonObject = null; - String appPath = MicroPhotoService.buildAppDir(this.getApplicationContext()); + String appPath = MicroPhotoContext.buildAppDir(this.getApplicationContext()); InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; File dataPath = new File(appPath + "data/channels/"); diff --git a/app/src/main/java/com/xypower/mpapp/MainActivity.java b/app/src/main/java/com/xypower/mpapp/MainActivity.java index 18160ca0..9c8030c2 100644 --- a/app/src/main/java/com/xypower/mpapp/MainActivity.java +++ b/app/src/main/java/com/xypower/mpapp/MainActivity.java @@ -4,13 +4,11 @@ import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; -import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.FileObserver; @@ -18,13 +16,13 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.StrictMode; import android.os.SystemClock; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.core.app.ActivityCompat; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.content.FileProvider; import android.os.Bundle; import android.telephony.SubscriptionManager; @@ -38,6 +36,7 @@ import android.view.WindowManager; import android.widget.Toast; import com.dowse.camera.client.DSCameraManager; +import com.xypower.common.MicroPhotoContext; import com.xypower.mpapp.databinding.ActivityMainBinding; import com.xypower.mpapp.utils.RandomReader; //import com.xinyingpower.microphoto.request.INettyMessageListener; @@ -54,8 +53,6 @@ import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; -import java.util.TimeZone; import org.json.JSONException; import org.json.JSONObject; @@ -209,6 +206,9 @@ public class MainActivity extends AppCompatActivity { int width = defaultDisplay.getWidth(); int height = defaultDisplay.getHeight(); + StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(policy); + Intent intent = getIntent(); final int noDelay = intent.getIntExtra("noDelay", 0); int rebootFlag = intent.getIntExtra("reboot", 0); @@ -491,7 +491,7 @@ public class MainActivity extends AppCompatActivity { // call the superclass method first super.onStart(); - String logFilePath = MicroPhotoService.buildAppDir(this.getApplicationContext()); + String logFilePath = MicroPhotoContext.buildAppDir(this.getApplicationContext()); logFilePath += "logs/log.txt"; mLogFileObserver = new LogFileObserver(logFilePath); @@ -557,7 +557,7 @@ public class MainActivity extends AppCompatActivity { AppConfig appConfig = new AppConfig(); - String appPath = MicroPhotoService.buildAppDir(context); + String appPath = MicroPhotoContext.buildAppDir(context); InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; @@ -606,7 +606,7 @@ public class MainActivity extends AppCompatActivity { private void saveAppConfig(String cmdid, String server, int port, int protocol, int networkProtocol) { - String appPath = MicroPhotoService.buildAppDir(this.getApplicationContext()); + String appPath = MicroPhotoContext.buildAppDir(this.getApplicationContext()); InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; OutputStreamWriter outputStreamWriter = null; diff --git a/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java b/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java index 24fea552..b3cae42a 100644 --- a/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java +++ b/app/src/main/java/com/xypower/mpapp/MicroPhotoService.java @@ -22,7 +22,6 @@ import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -45,9 +44,12 @@ import android.widget.Toast; import com.dev.devapi.api.SysApi; import com.xypower.common.FileDownloader; +import com.xypower.common.InetAddressUtils; +import com.xypower.common.MicroPhotoContext; import java.io.File; import java.lang.reflect.Method; +import java.net.InetAddress; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -255,7 +257,7 @@ public class MicroPhotoService extends Service { int preset = (int) ((val & 0xFF00L) >> 8); Log.i(TAG, "PhotoTimer Fired: CH=" + channel + " PR=" + preset); - mService.notifyToTakePhoto(mService.mNativeHandle, channel, preset, ts, mService.buildPhotoDir(mService.getApplicationContext(), channel), mService.buildPhotoFileName(channel, preset, ts), true); + mService.notifyToTakePhoto(mService.mNativeHandle, channel, preset, ts, true); } } @@ -277,7 +279,7 @@ public class MicroPhotoService extends Service { long ts = System.currentTimeMillis() / 1000; Log.i(TAG, "Take Photo CH=" + channel + " PR=" + preset + " Mannually"); - mService.notifyToTakePhoto(mService.mNativeHandle, channel, preset, ts, mService.buildPhotoDir(mService.getApplicationContext(), channel), mService.buildPhotoFileName(channel, preset, ts), photoOrVideo); + mService.notifyToTakePhoto(mService.mNativeHandle, channel, preset, ts, photoOrVideo); } else if (TextUtils.equals(ACTION_TIMEOUT, action)) { long uid = intent.getLongExtra(EXTRA_PARAM_TIMER_UID, 0); long expectedTimes = intent.getLongExtra(EXTRA_PARAM_TIMES, 0); @@ -524,18 +526,31 @@ public class MicroPhotoService extends Service { mMessenger = intent.getParcelableExtra("messenger"); } - String appPath = buildAppDir(this.getApplicationContext()); + String appPath = MicroPhotoContext.buildAppDir(this.getApplicationContext()); - String ip = intent.getStringExtra("server"); + String server = intent.getStringExtra("server"); int port = intent.getIntExtra("port", 0); String cmdid = intent.getStringExtra("cmdid"); int protocol = intent.getIntExtra("protocol", 0); int networkProtocol = intent.getIntExtra("networkProtocol", 0); - Log.i(TAG, "AppPath=" + appPath + " Server=" + ip + ":" + port + " cmdid=" + cmdid + " Protocol=" + protocol + " Network=" + networkProtocol); + if (!InetAddressUtils.isIPv4Address(server) && !InetAddressUtils.isIPv6Address(server)) { + // It is a domain + InetAddress addr = null; + try { + addr = InetAddress.getByName(server); + } catch (Exception e) { + e.printStackTrace(); + } + if (addr != null) { + server = addr.getHostAddress(); + } + } + Log.i(TAG, "AppPath=" + appPath + " Server=" + server + ":" + port + " cmdid=" + cmdid + " Protocol=" + protocol + " Network=" + networkProtocol); MicroPhotoService service = MicroPhotoService.this; - service.mNativeHandle = init(appPath, ip, port, cmdid, protocol, networkProtocol); + + service.mNativeHandle = init(appPath, server, port, cmdid, protocol, networkProtocol); if (service.mNativeHandle !=0) { service.mCmdid = cmdid; @@ -676,58 +691,6 @@ public class MicroPhotoService extends Service { return notificationBuilder.build(); } - public static String buildAppDir(Context contxt) { - - /* - File[] paths = contxt.getExternalFilesDirs(null); - - if (paths == null || paths.length == 0) { - return null; - } - - File path = paths[0]; - */ - - File path = new File(Environment.getExternalStorageDirectory(), contxt.getPackageName() + "/"); - - if (!path.exists() && !path.mkdirs()) { - return null; - } - String p = path.getAbsolutePath(); - if (!p.endsWith(File.separator)) { - p += File.separator; - } - return p; - } - - public static String buildPhotoDir(Context contxt, int channel) { - // File path = new File(Environment.getExternalStorageDirectory(), "com.xinyingpower.mp/photos/"); - - String appDir = buildAppDir(contxt); - if (appDir == null) { - return null; - } - - File path = new File(appDir, "photos/"); - - if (!path.exists() && !path.mkdirs()) { - return null; - } - String p = path.getAbsolutePath(); - if (!p.endsWith(File.separator)) { - p += File.separator; - } - return p; - } - - private String buildPhotoFileName(int channel, int preset, long ts) { - - LocalDateTime nowDT = LocalDateTime.now(); - String date = nowDT.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss.S")); - String photoFile = "img_" + Integer.toString(channel) + "_" + Integer.toHexString(preset).toUpperCase() + "_" + date + ".jpg"; - return photoFile; - } - public boolean updateTime(long timeInMillis) { try { SysApi.setSystemTime(getApplicationContext(), timeInMillis); @@ -773,7 +736,7 @@ public class MicroPhotoService extends Service { public void downloadAndInstall(final String url) { final Context context = getApplicationContext(); - final String tempPath = buildAppDir(context) + File.separator + "tmp"; + final String tempPath = MicroPhotoContext.buildAppDir(context) + File.separator + "tmp"; File file = new File(tempPath); file.mkdirs(); final String filePath = tempPath + File.separator + "mp.apk"; @@ -919,7 +882,7 @@ cellSignalStrengthGsm.getDbm(); protected native long getHeartbeatDuration(long handler); protected native long[] getPhotoTimeData(long handler); // protected native long[] getNextScheduleItem(long handler); - protected native boolean notifyToTakePhoto(long handler, int channel, int preset, long scheduleTime, String path, String fileName, boolean sendToCma); + protected native boolean notifyToTakePhoto(long handler, int channel, int preset, long scheduleTime, boolean sendToCma); protected native boolean sendHeartbeat(long handler); protected native boolean fireTimeout(long handler, long uid, long times); protected native void updatePosition(long handler, double lon, double lat, long ts); diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index ecf21d72..5b6de180 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -25,7 +25,8 @@ android:layout_marginLeft="4dp" android:layout_marginTop="8dp" android:ems="10" - android:inputType="none" + android:maxLines="1" + android:inputType="text" android:imeOptions="actionDone" android:singleLine="true" android:text="XY-ANDROIDSIM-001" @@ -58,7 +59,8 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:ems="10" - android:inputType="none" + android:maxLines="1" + android:inputType="text" android:imeOptions="actionDone" android:text="47.96.238.157" app:layout_constraintStart_toStartOf="@+id/cmdid" @@ -71,6 +73,7 @@ android:ems="10" android:inputType="none|number" android:imeOptions="actionDone" + android:maxLines="1" android:text="6891" app:layout_constraintBottom_toBottomOf="@+id/server" app:layout_constraintLeft_toRightOf="@+id/server" diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 900228d6..dbed5641 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -5,6 +5,7 @@ @color/purple_200 @color/purple_700 @color/teal_200 + 12sp \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 7af1342b..9f40e5b4 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -5,7 +5,7 @@ @color/purple_500 @color/purple_700 @color/teal_200 - 14sp + 12sp \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle index db159a66..b9a578f6 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -32,5 +32,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - implementation files('libs/devapi.aar') + compileOnly files('libs/devapi.aar') } \ No newline at end of file diff --git a/common/src/main/java/com/xypower/common/InetAddressUtils.java b/common/src/main/java/com/xypower/common/InetAddressUtils.java new file mode 100644 index 00000000..3cf0bd46 --- /dev/null +++ b/common/src/main/java/com/xypower/common/InetAddressUtils.java @@ -0,0 +1,28 @@ +package com.xypower.common; + +import java.util.regex.Pattern; + +public class InetAddressUtils { + // String regex = "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$"; + private static final Pattern IPV4_PATTERN = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); + + private static final Pattern IPV6_STD_PATTERN = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); + + private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); + + public static boolean isIPv4Address(final String input) { + return IPV4_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6StdAddress(final String input) { + return IPV6_STD_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6HexCompressedAddress(final String input) { + return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); + } + + public static boolean isIPv6Address(final String input) { + return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); + } +} diff --git a/common/src/main/java/com/xypower/common/MicroPhotoContext.java b/common/src/main/java/com/xypower/common/MicroPhotoContext.java index edbe7dad..31b6ffdc 100644 --- a/common/src/main/java/com/xypower/common/MicroPhotoContext.java +++ b/common/src/main/java/com/xypower/common/MicroPhotoContext.java @@ -1,4 +1,27 @@ package com.xypower.common; +import android.content.Context; +import android.os.Environment; + +import java.io.File; + public class MicroPhotoContext { + + public static final String PACKAGE_NAME_MPAPP = "com.xypower.mpapp"; + public static final String PACKAGE_NAME_MPMASTER = "com.xypower.mpmaster"; + public final static String MASTER_URL = "http://180.166.218.222:40101/?cmdid="; + + public static String buildAppDir(Context contxt) { + + File path = new File(Environment.getExternalStorageDirectory(), contxt.getPackageName() + "/"); + + if (!path.exists() && !path.mkdirs()) { + return null; + } + String p = path.getAbsolutePath(); + if (!p.endsWith(File.separator)) { + p += File.separator; + } + return p; + } } diff --git a/mpmaster/build.gradle b/mpmaster/build.gradle index 8cd8ab9a..f2f1953f 100644 --- a/mpmaster/build.gradle +++ b/mpmaster/build.gradle @@ -36,4 +36,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation files('libs/devapi.aar') } \ No newline at end of file diff --git a/mpmaster/libs/devapi.aar b/mpmaster/libs/devapi.aar new file mode 100644 index 00000000..743280f4 Binary files /dev/null and b/mpmaster/libs/devapi.aar differ diff --git a/mpmaster/src/main/AndroidManifest.xml b/mpmaster/src/main/AndroidManifest.xml index a59fbf69..2d6bde42 100644 --- a/mpmaster/src/main/AndroidManifest.xml +++ b/mpmaster/src/main/AndroidManifest.xml @@ -7,15 +7,20 @@ + + @@ -29,7 +34,7 @@ + android:exported="true"> diff --git a/mpmaster/src/main/java/com/xypower/mpmaster/AppMaster.java b/mpmaster/src/main/java/com/xypower/mpmaster/AppMaster.java new file mode 100644 index 00000000..9f50428f --- /dev/null +++ b/mpmaster/src/main/java/com/xypower/mpmaster/AppMaster.java @@ -0,0 +1,211 @@ +package com.xypower.mpmaster; + +import android.content.Context; +import android.os.PowerManager; +import android.text.TextUtils; +import android.util.Pair; + +import com.dev.devapi.api.SysApi; +import com.xypower.common.FileDownloader; +import com.xypower.common.MicroPhotoContext; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +public class AppMaster { + + private MicroPhotoService mService; + private String mCmdid; + private PowerManager.WakeLock mWakelock; + + public AppMaster(MicroPhotoService service, String cmdid) { + + PowerManager powerManager = (PowerManager) service.getSystemService(Context.POWER_SERVICE); + mWakelock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "com.xinyingpower.microphoto:Upgrader"); + + mService = service; + mCmdid = cmdid; + } + + @Override + protected void finalize() { + try { + if (mWakelock != null) { + mWakelock.release(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + mWakelock = null; + } + mService = null; + } + + public void start() { + + new Thread(new Runnable() { + @Override + public void run() { + HttpURLConnection httpURLConnection = null; + InputStream inputStream = null; + + try { + String url = MicroPhotoContext.MASTER_URL + URLEncoder.encode(mCmdid, "UTF-8"); + URL mUrl = new URL(url); + httpURLConnection = (HttpURLConnection) mUrl.openConnection(); + httpURLConnection.setConnectTimeout(15000); + httpURLConnection.setReadTimeout(15000); + httpURLConnection.setRequestMethod("POST"); + // httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); + httpURLConnection.setDoInput(true); + httpURLConnection.setDoOutput(true); + + List> postParams = new ArrayList<>(); + postParams.add(new Pair("id", mCmdid)); + // postParams(httpURLConnection.getOutputStream(), postParams); + buildParams(httpURLConnection.getOutputStream()); + httpURLConnection.connect(); + inputStream = httpURLConnection.getInputStream(); + + int responseCode = httpURLConnection.getResponseCode(); + + if (responseCode == HttpURLConnection.HTTP_OK) { + //在子线程中不能操作UI线程,通过handler在UI线程中进行操作 + // handler.sendEmptyMessage(0x00); + String response = convertStreamToString(inputStream); + process(response); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + }).start(); + } + + private void process(String content) { + if (TextUtils.isEmpty(content)) { + return; + } + + try { + JSONObject jsonObject = new JSONObject(content); + int isUpgrade = jsonObject.optInt("isUpgrade", 0); + String url = jsonObject.optString("url", null); + + if (isUpgrade == 1 && !TextUtils.isEmpty(url)) { + upgradeApp(url); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + private void upgradeApp(String url) { + + FileDownloader dl = new FileDownloader(); + File path = new File(MicroPhotoContext.buildAppDir(mService.getApplicationContext()), "packages"); + if (!path.exists()) { + path.mkdirs(); + } + + File file = new File(path, "app.apk"); + if (file.exists()) { + file.delete(); + } + String apkPath = file.getAbsolutePath(); + if (dl.download(url, apkPath)) { + Context context = mService.getApplicationContext(); + SysApi.installApk(context, apkPath, context.getPackageName(), true); + } + } + + private void buildParams(OutputStream output) { + BufferedWriter bufferedWriter = null; + try { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", mCmdid); + + bufferedWriter = new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); + bufferedWriter.write(jsonObject.toString()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (bufferedWriter != null) { + bufferedWriter.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public static void postParams(OutputStream output, List> paramsList) { + + BufferedWriter bufferedWriter = null; + try { + StringBuilder stringBuilder = new StringBuilder(); + for (Pair pair : paramsList) { + if (!TextUtils.isEmpty(stringBuilder)) { + stringBuilder.append("&"); + } + stringBuilder.append(URLEncoder.encode(pair.first, "UTF-8")); + stringBuilder.append("="); + stringBuilder.append(URLEncoder.encode(pair.second, "UTF-8")); + bufferedWriter = new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); + bufferedWriter.write(stringBuilder.toString()); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (bufferedWriter != null) { + bufferedWriter.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private String convertStreamToString(InputStream inputStream) { + BufferedReader bufferedReader = null; + StringBuffer stringBuffer = new StringBuffer(); + String line; + try { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + while ((line = bufferedReader.readLine()) != null) { + stringBuffer.append(line).append("\n"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return stringBuffer.toString(); + } + + +} diff --git a/mpmaster/src/main/java/com/xypower/mpmaster/MicroPhotoService.java b/mpmaster/src/main/java/com/xypower/mpmaster/MicroPhotoService.java new file mode 100644 index 00000000..aff437a5 --- /dev/null +++ b/mpmaster/src/main/java/com/xypower/mpmaster/MicroPhotoService.java @@ -0,0 +1,16 @@ +package com.xypower.mpmaster; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class MicroPhotoService extends Service { + public MicroPhotoService() { + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. + throw new UnsupportedOperationException("Not yet implemented"); + } +} \ No newline at end of file