From cd16668afd42328e9cdbc4ed485088f1c8678705 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 8 Oct 2023 09:12:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E5=BD=A2=E6=84=8F=E7=9A=84so?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=83=A8=E5=88=86=E7=B3=BB=E7=BB=9F=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 5 +- app/libs/devapi.aar | Bin 0 -> 42083 bytes app/src/main/cpp/CMakeLists.txt | 3 +- app/src/main/cpp/MicroPhoto.cpp | 18 +- app/src/main/cpp/PhoneDevice.cpp | 187 +++++++++++++++--- app/src/main/cpp/PhoneDevice.h | 11 ++ app/src/main/cpp/camera2/OpenCVHdr.h | 87 ++++++++ app/src/main/cpp/camera2/ndkcamera.cpp | 84 ++++---- app/src/main/cpp/camera2/ndkcamera.h | 1 + .../microphoto/FileDownloader.java | 84 ++++++++ .../microphoto/FloatingWindow.java | 4 +- .../microphoto/MicroPhotoService.java | 158 ++++++++++++++- .../microphoto/PositionManager.java | 32 +++ 13 files changed, 592 insertions(+), 82 deletions(-) create mode 100644 app/libs/devapi.aar create mode 100644 app/src/main/cpp/camera2/OpenCVHdr.h create mode 100644 app/src/main/java/com/xinyingpower/microphoto/FileDownloader.java create mode 100644 app/src/main/java/com/xinyingpower/microphoto/PositionManager.java diff --git a/app/build.gradle b/app/build.gradle index 2dcb550b..ab97dd6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,8 +2,8 @@ plugins { id 'com.android.application' } -def AppVersionName = "1.0.7" -def AppVersionCode = ((1 * 100 + 1) * 100 + 0) * 10 + 7 +def AppVersionName = "1.0.8" +def AppVersionCode = ((1 * 100 + 1) * 100 + 0) * 10 + 8 android { namespace 'com.xinyingpower.microphoto' @@ -74,6 +74,7 @@ dependencies { // implementation 'com.tencent.mars:mars-core:1.2.5' // implementation 'com.tencent:mmkv-static:1.3.0' // implementation project(path: ':opencv') + implementation files('libs/devapi.aar') } \ No newline at end of file diff --git a/app/libs/devapi.aar b/app/libs/devapi.aar new file mode 100644 index 0000000000000000000000000000000000000000..743280f499aded8ae4bd64e02ec48098986c7fae GIT binary patch literal 42083 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

fW*Ffgc%`gNZ1m1G3b&K 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 Lmo_i>PwRgHkI~xP literal 0 HcmV?d00001 diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 58acfe94..62150282 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -285,6 +285,7 @@ add_library( # Sets the name of the library. ${TERM_CORE_ROOT}/SpecData_ZJ.cpp ${TERM_CORE_ROOT}/Timer.cpp ${TERM_CORE_ROOT}/TimerThread.cpp + ${TERM_CORE_ROOT}/Template.cpp ${TERM_CORE_ROOT}/Utils.cpp ${TERM_CORE_ROOT}/Client/TerminalService.cpp ${TERM_CORE_ROOT}/Client/Terminal.cpp @@ -327,7 +328,7 @@ target_link_libraries( # Specifies the target library. # included in the NDK. ${log-lib} - android camera2ndk mediandk + android camera2ndk mediandk z ncnn ${OpenCV_LIBS} diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp index b11e375e..fa1e3f15 100644 --- a/app/src/main/cpp/MicroPhoto.cpp +++ b/app/src/main/cpp/MicroPhoto.cpp @@ -152,7 +152,6 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_notifyToTakePhoto( return JNI_TRUE; } - extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_sendHeartbeat( JNIEnv* env, @@ -170,7 +169,6 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_sendHeartbeat( return JNI_TRUE; } - extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_fireTimeout( JNIEnv* env, @@ -187,6 +185,22 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_fireTimeout( return phoneDevice->FireTimer((IDevice::timer_uid_t)uid, static_cast(times)) ? JNI_TRUE : JNI_FALSE; } +extern "C" JNIEXPORT void JNICALL +Java_com_xinyingpower_microphoto_MicroPhotoService_updatePosition( + JNIEnv* env, + jobject pThis, jlong handler, jdouble lon, jdouble lat, jlong ts) { + + CTerminal* pTerminal = reinterpret_cast(handler); + IDevice* dev = pTerminal->GetDevice(); + if (dev == NULL) + { + return; + } + + CPhoneDevice* phoneDevice = (CPhoneDevice *)dev; + phoneDevice->UpdatePosition(lon, lat, ts); +} + extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_uninit( JNIEnv* env, diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index 3257f845..14ae85f1 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "CameraTestHelpers" #include #include "PhoneDevice.h" +#include #include #include @@ -180,6 +181,11 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service) mRequestWakelockMid = env->GetMethodID(classService, "requestWakelock", "(Ljava/lang/String;J)V"); mReleaseWakelockMid = env->GetMethodID(classService, "releaseWakelock", "(Ljava/lang/String;)V"); + mGetPowerInfoMid = env->GetMethodID(classService, "getPowerInfo", "()Ljava/lang/String;"); + mRebootMid = env->GetMethodID(classService, "reboot", "()V"); + mEnableGpsMid = env->GetMethodID(classService, "enableGps", "(Z)V"); + mRequestPositionMid = env->GetMethodID(classService, "requestPosition", "()Z"); + env->DeleteLocalRef(classService); if (didAttachThread) @@ -238,37 +244,83 @@ bool CPhoneDevice::UpdateSchedules() return true; } -bool CPhoneDevice::QuerySystemProperties(map& properties) +bool CPhoneDevice::QuerySystemProperties(std::map& properties) { char value[PROP_VALUE_MAX] = { 0 }; - __system_property_get("ro.telephony.default_network", value); + std::map powerInfo; - __system_property_get("ro.product.name", value); - properties[PROP_EQUIP_NAME] = value; + for (std::map::iterator it = properties.begin(); it != properties.end(); ++it) + { + if (it->first == PROP_EQUIP_NAME) + { + __system_property_get("ro.product.name", value); + it->second = value; + } + else if (it->first == PROP_MODEL) + { + __system_property_get("ro.product.model", value); + it->second = value; + } + else if (it->first == PROP_BS_MANU) + { + __system_property_get("ro.product.manufacturer", value); + it->second = value; + } + else if (it->first == PROP_VERSION) { + __system_property_get("ro.build.version.release", value); + it->second = value; + } + else if (it->first == PROP_PROD_DATE) { + __system_property_get("ro.build.date.utc", value); + it->second = value; + } + else if (it->first == PROP_SN) + { + __system_property_get("ro.serialno", value); + it->second = value; + } + else if (it->first == PROP_IMEI) + { + __system_property_get("phone.imei", value); + it->second = value; + } - __system_property_get("ro.product.model", value); - properties[PROP_MODEL] = value; + properties[PROP_BS_ID] = "SHXY"; - __system_property_get("ro.product.manufacturer", value); - properties[PROP_BS_MANU] = value; - properties[PROP_BS_ID] = "SHXY"; - __system_property_get("ro.build.version.release", value); - properties[PROP_VERSION] = value; - __system_property_get("ro.build.date.utc", value); - properties[PROP_PROD_DATE] = value; + } - __system_property_get("ro.serialno", value); - properties[PROP_SN] = value; + __system_property_get("ro.telephony.default_network", value); - __system_property_get("phone.imei", value); - properties[PROP_IMEI] = value; return true; } +void CPhoneDevice::QueryPowerInfo(std::map& powerInfo) +{ + JNIEnv* env = NULL; + jboolean ret = JNI_FALSE; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } + jobject jobj = env->CallObjectMethod(m_javaService, mGetPowerInfoMid); + std::string str = jstring2string(env, (jstring)jobj); + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + if (!str.empty()) + { + std::map queries = parseQuery(str); + powerInfo.swap(queries); + } +} bool CPhoneDevice::GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector& items) { @@ -277,7 +329,58 @@ bool CPhoneDevice::GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTi bool CPhoneDevice::Reboot() { - return false; + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } + env->CallVoidMethod(m_javaService, mRebootMid); + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + return true; +} + +bool CPhoneDevice::EnableGPS(bool enabled) +{ + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + jboolean jenabled = enabled ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(m_javaService, mEnableGpsMid, jenabled); + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } +} + +bool CPhoneDevice::RequestPosition() +{ + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + + jboolean ret = env->CallBooleanMethod(m_javaService, mRequestPositionMid); + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + return (ret == JNI_TRUE); } IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout, unsigned long times/* = 0*/) @@ -493,17 +596,44 @@ bool CPhoneDevice::OnImageReady(const cv::Mat& mat) const double width = mat.size().width; double ratio = std::min(height / 1024, width / 1920); fontScale = fontScale * ratio; - double thickness1 = 4 * ratio; - double thickness2 = 2 * ratio; + + int thickness2 = 1 * ratio; + if (thickness2 == 0) thickness2 = 1; + int thickness1 = thickness2 + 1; + + cv::Scalar scalar1(0, 0, 0); // black + cv::Scalar scalar2(255, 255, 255); // white + cv::Point pt1, pt2; for (vector::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it) { - // getTextSize(value, font, scale, 1, &bottom); textSize = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, fontScale, thickness1, &baseline); - cv::Point pt(it->x * ratio, it->y * ratio + textSize.height); - putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, fontScale, cv::Scalar(0, 0, 0), thickness1,cv::LINE_AA); textSize2 = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, fontScale, thickness2, &baseline); - pt.y -= (textSize.height - textSize2.height) / 2; - putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, fontScale, cv::Scalar(255, 255, 255), thickness2,cv::LINE_AA); + + ALOGE("putText: w1=%d w2=%d tn=%d/%d", textSize.width, textSize2.width, thickness1, thickness2); + + if (it->alignment == OSD_ALIGNMENT_TOP_LEFT) + { + pt1.x = it->x * ratio; + pt1.y = it->y * ratio + textSize.height; + } + else if (it->alignment == OSD_ALIGNMENT_TOP_RIGHT) + { + pt1.x = width - textSize.width - it->x * ratio; + pt1.y= it->y * ratio + textSize.height; + } + else if (it->alignment == OSD_ALIGNMENT_BOTTOM_RIGHT) + { + pt1.x = width - textSize.width - it->x * ratio; + pt1.y = height - it->y * ratio; + } + else if (it->alignment == OSD_ALIGNMENT_BOTTOM_LEFT) + { + pt1.x = it->x * ratio; + pt1.y = height - it->y * ratio; + } + + cv::putText(mat, it->text, pt1, cv::FONT_HERSHEY_COMPLEX, fontScale, scalar1, thickness1,cv::LINE_AA); + cv::putText(mat, it->text, pt1, cv::FONT_HERSHEY_COMPLEX, fontScale, scalar2, thickness2,cv::LINE_AA); } vector compression_params; @@ -550,3 +680,10 @@ std::string CPhoneDevice::GetFileName() const return mPath; } +void CPhoneDevice::UpdatePosition(double lon, double lat, time_t ts) +{ + if (m_listener != NULL) + { + return m_listener->OnPositionDataArrived(lon, lat, ts); + } +} \ No newline at end of file diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index 0a828862..f2b2b58a 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -36,6 +36,8 @@ public: virtual bool UpdateSchedules(); virtual bool QuerySystemProperties(map& properties); virtual bool Reboot(); + virtual bool EnableGPS(bool enabled); + virtual bool RequestPosition(); virtual timer_uid_t RegisterHeartbeat(unsigned int timerType, unsigned int timeout); virtual bool TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector& osds, const string& path); virtual bool CloseCamera(); @@ -48,6 +50,7 @@ public: bool GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector& items); + void UpdatePosition(double lon, double lat, time_t ts); protected: std::string GetFileName() const; @@ -65,6 +68,8 @@ protected: return false; } + void QueryPowerInfo(std::map& powerInfo); + bool OnImageReady(const cv::Mat& mat) const; void onError(const std::string& msg) const; @@ -81,6 +86,12 @@ protected: jmethodID mRequestWakelockMid; jmethodID mReleaseWakelockMid; + jmethodID mGetPowerInfoMid; + + jmethodID mRebootMid; + jmethodID mEnableGpsMid; + jmethodID mRequestPositionMid; + std::string mPath; IDevice::PHOTO_INFO mPhotoInfo; vector mOsds; diff --git a/app/src/main/cpp/camera2/OpenCVHdr.h b/app/src/main/cpp/camera2/OpenCVHdr.h new file mode 100644 index 00000000..f79ef57c --- /dev/null +++ b/app/src/main/cpp/camera2/OpenCVHdr.h @@ -0,0 +1,87 @@ +#ifndef __OPENCV_HDR_H__ +#define __OPENCV_HDR_H__ + +#include +#include +#include +#include +#include +using namespace std; +using namespace cv; + + +void Debevec(vectorexposureImages, vectorexposureTimes, Mat& output); +void Robertson(vectorexposureImages, vectorexposureTimes, Mat& output); +void Mertens(vectorexposureImages, Mat& output); + +//使用对比度、饱和度和曝光度好坏来对像素加权,而不是使用拉普拉斯金字塔来对图像进行加权。得到的图像权重被构造为对比度、饱和度和曝光度良好度量的加权平均值。 +//生成的图像不需要进行色调映射,并且可以通过乘以255转换为8位图像,但是建议应用伽马校正或线性色调映射。 +//不需要对应曝光时间, 可以在不需要HDR图像时使用,可直接得到低动态范围图像 +void Mertens(vectorexposureImages) +{ + auto merge_mertens = createMergeMertens(); + Mat fusion; + merge_mertens->process(exposureImages, fusion); + //进行伽马矫正,需根据实际图像调节参数,2.2f可满足大多数显示情况 + /*auto tonemap = createTonemap(2.2f); + tonemap->process(fusion, fusion);*/ + fusion = fusion * 255; + fusion.convertTo(fusion, CV_8UC3); + imshow("Mertens", fusion); + imwrite("D:\\opencv_c++\\Learning-OpenCV\\高动态(HDR)图像\\Mertens.png", fusion); +} + + +//使用Debevec方法进行HDR合成,将多张曝光时间不同的照片及其对应的曝光值进行加权平均,得到float32类型的高动态范围HDR图像 +//需要传入曝光图像序列和对应的曝光时间 +void Debevec(vectorexposureImages, vectorexposureTimes) +{ + //auto Debevec_response = createCalibrateDebevec(); + //Mat response; + //Debevec_response->process(exposureImages, response, exposureTimes); + + auto merge_Debevec = createMergeDebevec(); + Mat hdr; + merge_Debevec->process(exposureImages, hdr, exposureTimes); + //由于是在普通的LDR显示器上进行显示,因此我们必须将HDR图像映射到保留大多数细节的8位范围的低动态范围LDR图像。 + //这是进行色调映射的主要目标,伽玛校正值设置为2.2f适用于大多数情况 + Mat ldr; + auto tonemap = createTonemap(2.2f); + tonemap->process(hdr, ldr); + //HDR图像进行色调映射后得到的LDR图像取值范围在 [ 0 , 1 ] ,所以乘255将范围拉伸到 [ 0 , 255 ] + ldr = ldr * 255; + //将float32类型转化为uchar8类型 + ldr.convertTo(ldr, CV_8UC3); + imshow("Debevec_HDR", hdr); + imshow("Debevec_LDR", ldr); + imwrite("D:\\opencv_c++\\Learning-OpenCV\\高动态(HDR)图像\\Debevec_LDR.png", ldr); +} + + +//使用Robertson方法进行HDR图像合成,将多张曝光时间不同的照片及其对应的曝光值进行加权平均,得到float32类型的高动态范围HDR图像 +//需要传入曝光图像序列和对应的曝光时间 +void Robertson(vectorexposureImages, vectorexposureTimes) +{ + //auto Robertson_response = createCalibrateRobertson(); + //Mat response; + //Robertson_response->process(exposureImages, response, exposureTimes); + + auto merge_Robertson = createMergeRobertson(); + Mat hdr; + merge_Robertson->process(exposureImages, hdr, exposureTimes); + //由于是在普通的LDR显示器上进行显示,因此我们必须将HDR图像映射到保留大多数细节的8位范围的低动态范围LDR图像。 + //这是进行色调映射的主要目标,伽玛校正值设置为2.2f适用于大多数情况 + Mat ldr; + auto tonemap = createTonemap(2.2f); + tonemap->process(hdr, ldr); + //HDR图像进行色调映射后得到的LDR图像取值范围在 [ 0 , 1 ] ,所以乘255将范围拉伸到 [ 0 , 255 ] + ldr = ldr * 255; + ldr.convertTo(ldr, CV_8UC3); + imshow("Robertson_HDR", hdr); + imshow("Robertson_LDR", ldr); + imwrite("D:\\opencv_c++\\Learning-OpenCV\\高动态(HDR)图像\\Robertson_LDR.png", ldr); + +} + + +#endif /* __OPENCV_HDR_H__ */ diff --git a/app/src/main/cpp/camera2/ndkcamera.cpp b/app/src/main/cpp/camera2/ndkcamera.cpp index dfba86e4..9305fc62 100644 --- a/app/src/main/cpp/camera2/ndkcamera.cpp +++ b/app/src/main/cpp/camera2/ndkcamera.cpp @@ -115,8 +115,7 @@ NdkCamera::~NdkCamera() } } -int NdkCamera::open(const char* cameraId) -{ +int NdkCamera::open(const char* cameraId) { __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "DBG::open %s", cameraId); // camera_facing = _camera_facing; @@ -130,25 +129,23 @@ int NdkCamera::open(const char* cameraId) DisplayDimension foundRes = disp; { - ACameraIdList* camera_id_list = 0; + ACameraIdList *camera_id_list = 0; ACameraManager_getCameraIdList(camera_manager, &camera_id_list); - for (int i = 0; i < camera_id_list->numCameras; ++i) - { - const char* id = camera_id_list->cameraIds[i]; - if (strcmp(id, cameraId) != 0) - { + for (int i = 0; i < camera_id_list->numCameras; ++i) { + const char *id = camera_id_list->cameraIds[i]; + if (strcmp(id, cameraId) != 0) { continue; } - ACameraMetadata* camera_metadata = 0; + ACameraMetadata * camera_metadata = 0; ACameraManager_getCameraCharacteristics(camera_manager, id, &camera_metadata); // query faceing acamera_metadata_enum_android_lens_facing_t facing = ACAMERA_LENS_FACING_FRONT; { - ACameraMetadata_const_entry e = { 0 }; + ACameraMetadata_const_entry e = {0}; ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_LENS_FACING, &e); - facing = (acamera_metadata_enum_android_lens_facing_t)e.data.u8[0]; + facing = (acamera_metadata_enum_android_lens_facing_t) e.data.u8[0]; } camera_facing = facing; @@ -170,18 +167,19 @@ int NdkCamera::open(const char* cameraId) // query orientation int orientation = 0; { - ACameraMetadata_const_entry e = { 0 }; + ACameraMetadata_const_entry e = {0}; ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SENSOR_ORIENTATION, &e); - orientation = (int)e.data.i32[0]; + orientation = (int) e.data.i32[0]; } camera_orientation = orientation; { - ACameraMetadata_const_entry e = { 0 }; - ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &e); + ACameraMetadata_const_entry e = {0}; + ACameraMetadata_getConstEntry(camera_metadata, + ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &e); // format of the data: format, width, height, input?, type int32 // DisplayDimension foundRes(4000, 4000); @@ -208,8 +206,10 @@ int NdkCamera::open(const char* cameraId) } { - ACameraMetadata_const_entry val = { 0 }; - camera_status_t status = ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, &val); + ACameraMetadata_const_entry val = {0}; + camera_status_t status = ACameraMetadata_getConstEntry(camera_metadata, + ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, + &val); if (status == ACAMERA_OK) { exposureRange.min_ = val.data.i64[0]; if (exposureRange.min_ < kMinExposureTime) { @@ -228,8 +228,10 @@ int NdkCamera::open(const char* cameraId) } { - ACameraMetadata_const_entry val = { 0 }; - camera_status_t status = ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE, &val); + ACameraMetadata_const_entry val = {0}; + camera_status_t status = ACameraMetadata_getConstEntry(camera_metadata, + ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE, + &val); if (status == ACAMERA_OK) { sensitivityRange.min_ = val.data.i32[0]; @@ -245,22 +247,17 @@ int NdkCamera::open(const char* cameraId) } { - ACameraMetadata_const_entry e = { 0 }; - ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_CONTROL_AVAILABLE_SCENE_MODES, &e); + ACameraMetadata_const_entry e = {0}; + ACameraMetadata_getConstEntry(camera_metadata, + ACAMERA_CONTROL_AVAILABLE_SCENE_MODES, &e); - for (int i = 0; i < e.count; i ++) - { - if (ACAMERA_CONTROL_SCENE_MODE_HDR == e.data.u8[i]) - { + for (int i = 0; i < e.count; i++) { + if (ACAMERA_CONTROL_SCENE_MODE_HDR == e.data.u8[i]) { hdrSupported = true; break; - } - else if (ACAMERA_CONTROL_SCENE_MODE_NIGHT == e.data.u8[i]) - { + } else if (ACAMERA_CONTROL_SCENE_MODE_NIGHT == e.data.u8[i]) { nightModeSupported = true; - } - else if (ACAMERA_CONTROL_SCENE_MODE_NIGHT_PORTRAIT == e.data.u8[i]) - { + } else if (ACAMERA_CONTROL_SCENE_MODE_NIGHT_PORTRAIT == e.data.u8[i]) { nightPortraitModeSupported = true; } } @@ -275,15 +272,18 @@ int NdkCamera::open(const char* cameraId) ACameraManager_deleteCameraIdList(camera_id_list); } - if (camera_id.empty() || !foundIt) - { + if (camera_id.empty() || !foundIt) { return 1; } - if (camera_id == "1") - { + // TODO: PATCH!!!! + if (camera_id == "1") { camera_facing = 1; } + if (camera_id == "2") + { + camera_orientation += 180; + } mCameraId = camera_id; @@ -326,7 +326,7 @@ int NdkCamera::open(const char* cameraId) { res = ACameraDevice_createCaptureRequest(camera_device, TEMPLATE_STILL_CAPTURE, &capture_request); - int32_t fpsRange[2] = {10,15}; + int32_t fpsRange[2] = {10,30}; res = ACaptureRequest_setEntry_i32(capture_request, ACAMERA_CONTROL_AE_TARGET_FPS_RANGE,2,fpsRange); bool usingAE = true; @@ -338,7 +338,7 @@ int NdkCamera::open(const char* cameraId) uint8_t aeLockOff = ACAMERA_CONTROL_AE_LOCK_OFF; - ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AE_LOCK, 1, &aeLockOff); + // ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AE_LOCK, 1, &aeLockOff); } else { uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_OFF; res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AE_MODE, 1, &aeMode); @@ -352,11 +352,12 @@ int NdkCamera::open(const char* cameraId) res = ACaptureRequest_setEntry_i64(capture_request, ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime); } - uint8_t afMode = ACAMERA_CONTROL_AF_MODE_CONTINUOUS_VIDEO; + // uint8_t afMode = ACAMERA_CONTROL_AF_MODE_CONTINUOUS_VIDEO; + uint8_t afMode = ACAMERA_CONTROL_AF_MODE_CONTINUOUS_PICTURE; res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AF_MODE, 1, &afMode); uint8_t awbMode = ACAMERA_CONTROL_AWB_MODE_AUTO; - res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AWB_MODE, 1, &awbMode); + // res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AWB_MODE, 1, &awbMode); if (hdrSupported) { uint8_t hdrMode = ACAMERA_CONTROL_SCENE_MODE_HDR; // ACAMERA_CONTROL_SCENE_MODE_HDR @@ -367,14 +368,13 @@ int NdkCamera::open(const char* cameraId) if (nightModeSupported) { uint8_t modeEnabled = 1; // ACAMERA_CONTROL_SCENE_MODE_HDR - res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_NIGHT, 1, &modeEnabled); + // res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_NIGHT, 1, &modeEnabled); } if (nightPortraitModeSupported) { uint8_t modeEnabled = 1; // ACAMERA_CONTROL_SCENE_MODE_HDR - res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_NIGHT_PORTRAIT, 1, &modeEnabled); + // res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_NIGHT_PORTRAIT, 1, &modeEnabled); } - res = ACameraOutputTarget_create(image_reader_surface, &image_reader_target); res = ACaptureRequest_addTarget(capture_request, image_reader_target); } diff --git a/app/src/main/cpp/camera2/ndkcamera.h b/app/src/main/cpp/camera2/ndkcamera.h index f96acb41..14e41d1b 100644 --- a/app/src/main/cpp/camera2/ndkcamera.h +++ b/app/src/main/cpp/camera2/ndkcamera.h @@ -73,6 +73,7 @@ public: RangeValue sensitivityRange; + private: ACameraManager* camera_manager; ACameraDevice* camera_device; diff --git a/app/src/main/java/com/xinyingpower/microphoto/FileDownloader.java b/app/src/main/java/com/xinyingpower/microphoto/FileDownloader.java new file mode 100644 index 00000000..114518e3 --- /dev/null +++ b/app/src/main/java/com/xinyingpower/microphoto/FileDownloader.java @@ -0,0 +1,84 @@ +package com.xinyingpower.microphoto; + +import android.text.TextUtils; +import android.util.Log; + +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; + +public class FileDownloader { + protected void download(String urlString, String filePath) { + File downloadFile = null; + if (TextUtils.isEmpty(urlString)) + return; + HttpURLConnection connection = null; + try { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + URL url = new URL(urlString); + connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(5000); + connection.setReadTimeout(60000); + connection.setDoInput(true); + InputStream is = connection.getInputStream(); + final File temp = new File(filePath); + if (temp.exists()) + temp.delete(); + temp.createNewFile(); + temp.setReadable(true, false); + temp.setWritable(true, false); + downloadFile = temp; + Log.d("download", "url " + urlString + "\n save to " + temp); + OutputStream os = new FileOutputStream(temp); + byte[] buf = new byte[8 * 1024]; + int len; + try { + while ((len = is.read(buf)) != -1) { + os.write(buf, 0, len); + } + os.flush(); + if (os instanceof FileOutputStream) { + ((FileOutputStream) os).getFD().sync(); + } + } finally { + closeSilently(os); + closeSilently(is); + } + Log.d("downloadAPK", "download complete url=" + urlString + ", fileSize= " + temp.length()); +// installPkg(this, temp, pkg); + } catch (Exception e) { + Log.w("downloadAPK", e); + if (downloadFile != null) + downloadFile.delete(); + + } finally { + if (connection != null) + connection.disconnect(); + } + } + + public static final void closeSilently(Object closeable) { + try { + if (closeable != null) { + if (closeable instanceof Closeable) { + ((Closeable) closeable).close(); + } else if (closeable instanceof Socket) { + ((Socket) closeable).close(); + } else if (closeable instanceof ServerSocket) { + ((ServerSocket) closeable).close(); + } else { + throw new IllegalArgumentException("Unknown object to close"); + } + } + } catch (IOException e) { + // ignore + } + } +} diff --git a/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java b/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java index bee385ea..0bb9e56b 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java +++ b/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java @@ -10,6 +10,7 @@ import android.os.IBinder; import android.text.Editable; import android.text.TextWatcher; import android.util.DisplayMetrics; +import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -55,7 +56,8 @@ public class FloatingWindow extends Service { mWindowManager.removeView(mView); } } catch (Exception ex) { - ex.printStackTrace(); + // ex.printStackTrace(); + Log.e("FW", "Exception " + ex.getMessage()); } super.onDestroy(); diff --git a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java index 628e21c0..e9d111f4 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java +++ b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java @@ -10,6 +10,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; @@ -24,6 +30,8 @@ import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; +import com.dev.devapi.api.SysApi; + import java.io.File; import java.lang.reflect.Method; import java.time.LocalDateTime; @@ -71,6 +79,8 @@ public class MicroPhotoService extends Service { private final Map mWakeLocks = new HashMap<>(); private int mHeartbeatDuration = 0; // 5m: 5 * 60 * 1000 private long mNextHeartbeatTime = 0; + + private PositionManager mPositionManager = null; private final Map mTimers = new HashMap<>(); private static int stateService = STATE_SERVICE.NOT_CONNECTED; public MicroPhotoService() { @@ -619,23 +629,100 @@ public class MicroPhotoService extends Service { String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); String photoFile = "img_" + Integer.toString(channel) + "_" + Integer.toHexString(preset).toUpperCase() + "_" + date + ".jpg"; return photoFile; - } public boolean updateTime(long timeInMillis) { - boolean res = false; try { - // Calendar c = Calendar.getInstance(); - // c.set(2010, 1, 1, 12, 00, 00); - AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - am.setTime(timeInMillis); - res = true; + SysApi.setSystemTime(getApplicationContext(), timeInMillis); } catch (Exception ex) { - int aa = 0; } return true; } + public boolean requestPosition() { + + try { + + if (mLocationManager == null) { + mLocationManager = (LocationManager) getSystemService (Context.LOCATION_SERVICE); + + if (!mLocationManager.isProviderEnabled(GPS_LOCATION_NAME)) { + return false; + } + mLocateType = mLocationManager.GPS_PROVIDER; + } + + Location location = mLocationManager.getLastKnownLocation(mLocateType); + if (location != null) { + updatePosition(mHandler, location.getLongitude(), location.getLatitude(), location.getTime()); + } + // Set Listener + mLocationManager.requestLocationUpdates(mLocateType, 100,0, locationListener); + + return true; + } catch (SecurityException ex) { + } + + return false; + } + public void downloadAndInstall(final String url) { + + final Context context = getApplicationContext(); + final String tempPath = buildAppDir(context) + File.separator + "tmp"; + File file = new File(tempPath); + file.mkdirs(); + final String filePath = tempPath + File.separator + "mp.apk"; + Thread th =new Thread(new Runnable() { + @Override + public void run() { + + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); + PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "com.xinyingpower.microphoto:Upgrader"); + + FileDownloader fd = new FileDownloader(); + fd.download(url, filePath); + + SysApi.installApk(context, filePath, context.getPackageName(), true); + + wl.release(); + } + }); + th.start(); + } + + public String getPowerInfo() { + StringBuilder sb = new StringBuilder(); + sb.append("CV=" + SysApi.getChargingVoltage()); // ChargeVol + sb.append("&CC=" + SysApi.getChargingCurrent()); // ChargeCurrent + sb.append("&CP="+ SysApi.getChargingPower()); // ChargePower: + sb.append("&CBV="+ SysApi.getChargingBusVoltage()); // ChargeBusVol + sb.append("&BV="+ SysApi.getBatteryVoltage()); // BatVol + sb.append("&BC="+ SysApi.getBatteryCurrent()); // BatCurrent + sb.append("&BP="+ SysApi.getBatteryPower()); // BattaryPower + sb.append("&BBV="+ SysApi.getBatteryBusVoltage()); // BattaryBusVol + + return sb.toString(); + } + + public void reboot() { + + Handler handler = new Handler(); + Runnable runnable = new Runnable() { + @Override + public void run() { + SysApi.reboot(getApplicationContext()); + } + }; + handler.postDelayed(runnable, 400); + } + public void enableGps(boolean enabled) { + SysApi.enableGps(getApplicationContext(), enabled); + } + + public void selectSimCard(int num) { + SysApi.selectSimCard4Data(getApplicationContext(), num); + } + /* TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); // for example value of first element @@ -651,9 +738,62 @@ cellSignalStrengthGsm.getDbm(); protected native boolean notifyToTakePhoto(long handler, int channel, int preset, long scheduleTime, String path, String fileName, 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); protected native boolean uninit(long handler); protected long mHandler = 0; - private AlarmReceiver alarmReceiver = null; private ScreenActionReceiver screenactionreceiver = null; + + ////////////////////////GPS//////////////////// + private static final String GPS_LOCATION_NAME = android.location.LocationManager.GPS_PROVIDER; + private LocationManager mLocationManager; + private String mLocateType; + + private LocationListener locationListener = new LocationListener() { + + @Override + public void onLocationChanged(Location location) { + updatePosition(mHandler, location.getLongitude(), location.getLatitude(), location.getTime()); + // Log.i(TAG, "Time: " + location.getTime()); + // Log.i(TAG, "经度:" + location.getLongitude()); + // Log.i(TAG, "纬度:" + location.getLatitude()); + // Log.i(TAG, "海拔:" + location.getAltitude()); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + switch (status) { + case LocationProvider.AVAILABLE: + // Toast.makeText(MainActivity.this, "onStatusChanged:当前GPS状态为可见状态", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.OUT_OF_SERVICE: + // Toast.makeText(MainActivity.this, "onStatusChanged:当前GPS状态为服务区外状态", Toast.LENGTH_SHORT).show(); + break; + case LocationProvider.TEMPORARILY_UNAVAILABLE: + // Toast.makeText(MainActivity.this, "onStatusChanged:当前GPS状态为暂停服务状态", Toast.LENGTH_SHORT).show(); + break; + } + } + + /** + * @param provider + */ + @Override + public void onProviderEnabled(String provider) { + // Toast.makeText(MainActivity.this, "onProviderEnabled:方法被触发", Toast.LENGTH_SHORT).show(); + requestPosition(); + } + + /** + * @param provider + */ + @Override + public void onProviderDisabled(String provider) { + + } + }; + + + + ////////////////////////GPS//////////////////// } \ No newline at end of file diff --git a/app/src/main/java/com/xinyingpower/microphoto/PositionManager.java b/app/src/main/java/com/xinyingpower/microphoto/PositionManager.java new file mode 100644 index 00000000..3a50dd47 --- /dev/null +++ b/app/src/main/java/com/xinyingpower/microphoto/PositionManager.java @@ -0,0 +1,32 @@ +package com.xinyingpower.microphoto; + +import android.content.Context; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.os.Bundle; + +public class PositionManager { + private static final String GPS_LOCATION_NAME = android.location.LocationManager.GPS_PROVIDER; + private LocationManager mLocationManager; + private boolean mIsGpsEnabled; + private String mLocateType; + private Context mContext; + + public PositionManager(Context context) { + mContext = context; + + } + + public boolean isGpsEnabled() { + return mIsGpsEnabled; + } + + public void getLocation() { + + } + + + +}