diff --git a/app/build.gradle b/app/build.gradle index 9a631f74..ee18033e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,8 +2,8 @@ plugins { id 'com.android.application' } -def AppVersionName = "1.0.0" -def AppVersionCode = ((1 * 100 + 1) * 100 + 0) * 10 + 0 +def AppVersionName = "1.0.7" +def AppVersionCode = ((1 * 100 + 1) * 100 + 0) * 10 + 7 android { namespace 'com.xinyingpower.microphoto' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 55d5f811..bc51bfd4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,9 +31,8 @@ - - + + diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 7de857a9..ec3c6dba 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -34,6 +34,8 @@ set(OPENCV_EXTRA_MODULES_PATH D:/Workspace/Github/opencv_contrib/modules) project("microphoto") +# message(FATAL_ERROR "OpenCV ${OpenCV_DIR}") + find_package(OpenCV REQUIRED core imgproc highgui) if(OpenCV_FOUND) include_directories(${OpenCV_INCLUDE_DIRS}) @@ -281,12 +283,13 @@ add_library( # Sets the name of the library. ${TERM_CORE_ROOT}/SpecData_I1_SHX.cpp ${TERM_CORE_ROOT}/SpecData_XY.cpp ${TERM_CORE_ROOT}/SpecData_ZJ.cpp - ${TERM_CORE_ROOT}/TermClient.cpp ${TERM_CORE_ROOT}/Timer.cpp ${TERM_CORE_ROOT}/TimerThread.cpp ${TERM_CORE_ROOT}/Utils.cpp + ${TERM_CORE_ROOT}/Client/TerminalService.cpp ${TERM_CORE_ROOT}/Client/Terminal.cpp ${TERM_CORE_ROOT}/Client/Terminal_HN.cpp + ${TERM_CORE_ROOT}/Client/Terminal_AH.cpp ${TERM_CORE_ROOT}/Client/Terminal_HEN_ZZ.cpp ${TERM_CORE_ROOT}/Client/Terminal_HEN.cpp ${TERM_CORE_ROOT}/Client/Terminal_SHX.cpp diff --git a/app/src/main/cpp/Camera.cpp b/app/src/main/cpp/Camera.cpp index 5684aeca..c65d1ead 100644 --- a/app/src/main/cpp/Camera.cpp +++ b/app/src/main/cpp/Camera.cpp @@ -21,10 +21,7 @@ #include "Camera.h" #include - -#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#include CCamera::CCamera() { diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp index fcc42eeb..b11e375e 100644 --- a/app/src/main/cpp/MicroPhoto.cpp +++ b/app/src/main/cpp/MicroPhoto.cpp @@ -1,6 +1,7 @@ #include #include -#include +#include +#include #include "TerminalDevice.h" #include "PhoneDevice.h" #include "PhoneDevice2.h" @@ -15,17 +16,17 @@ #include "Camera.h" #include "Camera2Reader.h" - -bool GetJniEnv(JavaVM *vm, JNIEnv **env) +bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread) { - bool did_attach_thread = false; + didAttachThread = false; *env = nullptr; // Check if the current thread is attached to the VM auto get_env_result = vm->GetEnv((void**)env, JNI_VERSION_1_6); if (get_env_result == JNI_EDETACHED) { - if (vm->AttachCurrentThread(env, NULL) == JNI_OK) { - did_attach_thread = true; + get_env_result = vm->AttachCurrentThread(env, NULL); + if (get_env_result == JNI_OK) { + didAttachThread = true; } else { // Failed to attach thread. Throw an exception if you want to. } @@ -34,10 +35,9 @@ bool GetJniEnv(JavaVM *vm, JNIEnv **env) // Unsupported JNI version. Throw an exception if you want to. } - return did_attach_thread; + return get_env_result == JNI_OK; } - // #include "client/linux/handler/exception_handler.h" // #include "client/linux/handler/minidump_descriptor.h" @@ -74,7 +74,6 @@ Java_com_xinyingpower_microphoto_MainActivity_takePhoto( unsigned char id = (unsigned char)channel - 1; - Camera2Reader *camera = new Camera2Reader(id); const char *pathStr = env->GetStringUTFChars(path, 0); const char *fileNameStr = env->GetStringUTFChars(fileName, 0); @@ -88,10 +87,10 @@ Java_com_xinyingpower_microphoto_MainActivity_takePhoto( return JNI_TRUE; } -extern "C" JNIEXPORT jboolean JNICALL +extern "C" JNIEXPORT jlong JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_init( JNIEnv* env, - jobject pThis, jstring appPath, jstring ip, jint port, jstring cmdid) { + jobject pThis, jstring appPath, jstring ip, jint port, jstring cmdid, jint protocol) { /* google_breakpad::MinidumpDescriptor descriptor("."); @@ -107,35 +106,48 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_init( JavaVM* vm = NULL; jint ret = env->GetJavaVM(&vm); // const string& appPath, const string& termId, const string& server, unsigned short port, const string& bindIp - CTermClient& service = CTermClient::GetService(); + // CTerminal* pTerminal = reinterpret_cast(handler); // CTerminalDevice* device = new CTerminalDevice(vm, pThis); // CPhoneDevice2* device = new CPhoneDevice2(vm, pThis); + + CTerminal* pTerminal = NewTerminal(GetCurrentProtocol()); + CPhoneDevice* device = new CPhoneDevice(vm, pThis); - bool res = service.InitService(appPathStr, cmdidStr, ipStr, (unsigned short)port, "", device); + device->SetListener(pTerminal); + + pTerminal->InitServerInfo(appPathStr, cmdidStr, ipStr, port); + pTerminal->SetPacketSize(1 * 1024); // 1K + bool res = pTerminal->Startup(device); env->ReleaseStringUTFChars(appPath, appPathStr); env->ReleaseStringUTFChars(ip, ipStr); env->ReleaseStringUTFChars(cmdid, cmdidStr); - return res ? JNI_TRUE : JNI_FALSE; + if (!res) + { + delete pTerminal; + pTerminal = NULL; + } + + return reinterpret_cast(pTerminal); } extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_notifyToTakePhoto( JNIEnv* env, - jobject pThis, jint channel, jint preset, jlong scheduleTime, jstring path, jstring fileName, jboolean sendToCma) { + jobject pThis, jlong handler, jint channel, jint preset, jlong scheduleTime, jstring path, jstring fileName, jboolean sendToCma) { if (channel < 1 || channel > 0xFF) { return JNI_FALSE; } - CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); + CTerminal* pTerminal = reinterpret_cast(handler); if (pTerminal == NULL) { return JNI_FALSE; } - pTerminal->NotifyToTakePhoto((unsigned int)channel, (unsigned int)preset, 0, (unsigned long)scheduleTime); + pTerminal->RequestCapture((unsigned int)channel, (unsigned int)preset, 0, (unsigned long)scheduleTime); return JNI_TRUE; } @@ -144,9 +156,10 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_notifyToTakePhoto( extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_sendHeartbeat( JNIEnv* env, - jobject pThis) { + jobject pThis, + jlong handler) { - CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); + CTerminal* pTerminal = reinterpret_cast(handler); if (pTerminal == NULL) { return JNI_FALSE; @@ -161,45 +174,58 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_sendHeartbeat( extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_fireTimeout( JNIEnv* env, - jobject pThis, jlong uid) { + jobject pThis, jlong handler, jlong uid, jlong times) { - IDevice* dev = CTermClient::GetService().GetDevice(); + CTerminal* pTerminal = reinterpret_cast(handler); + IDevice* dev = pTerminal->GetDevice(); if (dev == NULL) { return JNI_FALSE; } CPhoneDevice* phoneDevice = (CPhoneDevice *)dev; - return phoneDevice->FireTimer((IDevice::timer_uid_t)uid) ? JNI_TRUE : JNI_FALSE; + return phoneDevice->FireTimer((IDevice::timer_uid_t)uid, static_cast(times)) ? JNI_TRUE : JNI_FALSE; } extern "C" JNIEXPORT jboolean JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_uninit( JNIEnv* env, - jobject pThis) { + jobject pThis, jlong handler) { - CTermClient::GetService().ExitService(); + CTerminal* pTerminal = reinterpret_cast(handler); + if (pTerminal == NULL) + { + return JNI_FALSE; + } + + pTerminal->SignalExit(); + pTerminal->Shutdown(); + + delete pTerminal; return JNI_TRUE; } - extern "C" JNIEXPORT jlong JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_getHeartbeatDuration( JNIEnv* env, - jobject pThis) { + jobject pThis, jlong handler) { - // CTermClient::GetService().ExitService(); + CTerminal* pTerminal = reinterpret_cast(handler); + if (pTerminal == NULL) + { + return DEFAULT_HEARTBEAT_DURATION; + } - return 60000; + return pTerminal->GetHeartbeatDuration(); } extern "C" JNIEXPORT jlongArray JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_getPhotoTimeData( JNIEnv* env, - jobject pThis) { + jobject pThis, jlong handler) { - CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); + CTerminal* pTerminal = reinterpret_cast(handler); if (pTerminal == NULL) { return NULL; @@ -259,13 +285,13 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_getPhotoTimeData( return data; } - +/* extern "C" JNIEXPORT jlongArray JNICALL Java_com_xinyingpower_microphoto_MicroPhotoService_getNextScheduleItem( JNIEnv* env, - jobject pThis) { + jobject pThis, jlong handler) { - CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); + CTerminal* pTerminal = reinterpret_cast(handler); if (pTerminal == NULL) { return NULL; @@ -324,3 +350,4 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_getNextScheduleItem( return data; } + */ diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index 86f0ea36..3257f845 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -17,9 +17,8 @@ */ #define LOG_TAG "CameraTestHelpers" - +#include #include "PhoneDevice.h" -#include "TermClient.h" #include #include @@ -34,17 +33,10 @@ #include #include -#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#define ASSERT(cond, fmt, ...) \ - if (!(cond)) { \ - __android_log_assert(#cond, LOG_TAG, fmt, ##__VA_ARGS__); \ - } - -extern bool GetJniEnv(JavaVM *vm, JNIEnv **env); +extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); +#define WAKELOCK_NAME "NDK_WK_" // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their // ranges // are normalized to eight bits. @@ -143,46 +135,73 @@ CPhoneDevice::CPhoneCamera::CPhoneCamera(CPhoneDevice* dev, int32_t width, int32 { } -void CPhoneDevice::CPhoneCamera::on_image(const cv::Mat& rgb) const +bool CPhoneDevice::CPhoneCamera::on_image(const cv::Mat& rgb) const { if (m_dev != NULL) { - m_dev->OnImageReady(rgb); + return m_dev->OnImageReady(rgb); + } + + return false; +} + +void CPhoneDevice::CPhoneCamera::on_error(const std::string& msg) const +{ + if (m_dev != NULL) + { + m_dev->onError(msg); } } CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service) { + mCamera = NULL; + m_listener = NULL; + mHeartbeatStartTime = 0; + mHeartbeatDuration = 0; + m_javaService = NULL; + m_vm = vm; JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } m_javaService = env->NewGlobalRef(service); - mHeartbeatStartTime = 0; - mHeartbeatDuration = 0; - jclass classService = env->GetObjectClass(m_javaService); - mRegisterTimerMid = env->GetMethodID(classService, "registerTimer", "(JI)Z"); + mRegisterTimerMid = env->GetMethodID(classService, "registerTimer", "(JIJ)Z"); mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(I)V"); mUnregisterTimerMid = env->GetMethodID(classService, "unregisterTimer", "(J)Z"); mUpdateTimeMid = env->GetMethodID(classService, "updateTime", "(J)Z"); + mRequestWakelockMid = env->GetMethodID(classService, "requestWakelock", "(Ljava/lang/String;J)V"); + mReleaseWakelockMid = env->GetMethodID(classService, "releaseWakelock", "(Ljava/lang/String;)V"); + env->DeleteLocalRef(classService); - if (attached) + if (didAttachThread) { vm->DetachCurrentThread(); } - m_timerUidFeed = time(NULL); + m_timerUidFeed = time(NULL) * 1000; + m_wakelockIdFeed = (unsigned long)m_timerUidFeed; } CPhoneDevice::~CPhoneDevice() { JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } env->DeleteGlobalRef(m_javaService); - if (attached) + if (didAttachThread) { m_vm->DetachCurrentThread(); } @@ -198,17 +217,27 @@ bool CPhoneDevice::UpdateTime(time_t ts) { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); - if (attached) + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } + jlong timeInMillis = ((jlong)ts) * 1000; + ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); + if (didAttachThread) { - jlong timeInMillis = ((jlong)ts) * 1000; - ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); m_vm->DetachCurrentThread(); } return (ret == JNI_TRUE); } +bool CPhoneDevice::UpdateSchedules() +{ + return true; +} + bool CPhoneDevice::QuerySystemProperties(map& properties) { char value[PROP_VALUE_MAX] = { 0 }; @@ -220,11 +249,9 @@ bool CPhoneDevice::QuerySystemProperties(map& properties) __system_property_get("ro.product.model", value); properties[PROP_MODEL] = value; - __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); @@ -239,7 +266,6 @@ bool CPhoneDevice::QuerySystemProperties(map& properties) __system_property_get("phone.imei", value); properties[PROP_IMEI] = value; - return true; } @@ -254,18 +280,23 @@ bool CPhoneDevice::Reboot() return false; } -IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout) +IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout, unsigned long times/* = 0*/) { IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1); ALOGI("NDK RegTimer: uid=%lld Type=%u timeout=%u", uid, timerType, timeout); JNIEnv* env = NULL; - jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); - if (attached) + 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, mRegisterTimerMid, (jlong)uid, (jint)timeout, (jlong)times); + if (didAttachThread) { - ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout); m_vm->DetachCurrentThread(); } @@ -280,12 +311,18 @@ IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigne bool CPhoneDevice::UnregisterTimer(IDevice::timer_uid_t uid) { - JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); - if (attached) + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return false; + } + ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid); + if (didAttachThread) { - ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid); m_vm->DetachCurrentThread(); } @@ -297,7 +334,67 @@ bool CPhoneDevice::UnregisterTimer(IDevice::timer_uid_t uid) return false; } -bool CPhoneDevice::FireTimer(timer_uid_t uid) +unsigned long CPhoneDevice::RequestWakelock(unsigned long timeout) +{ + unsigned long wakelockId = m_wakelockIdFeed.fetch_add(1); + std::string name = WAKELOCK_NAME; + name += to_string(wakelockId); + + ALOGI("RequestWakelock=%lld",wakelockId); + + jboolean ret = JNI_FALSE; + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + jstring jname = env->NewStringUTF(name.c_str()); + jlong jtimeout = (jlong)timeout; + + env->CallVoidMethod(m_javaService, mRequestWakelockMid, jname, jtimeout); + // env->ReleaseStringUTFChars(jname, name.c_str()); + env->DeleteLocalRef(jname); + + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + return wakelockId; +} + +bool CPhoneDevice::ReleaseWakelock(unsigned long wakelock) +{ + ALOGI("ReleaseWakelock=%lld", wakelock); + std::string name = WAKELOCK_NAME; + name += to_string(wakelock); + + jboolean ret = JNI_FALSE; + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return false; + } + jstring jname = env->NewStringUTF(name.c_str()); + + env->CallVoidMethod(m_javaService, mReleaseWakelockMid, jname); + env->DeleteLocalRef(jname); + // env->ReleaseStringUTFChars(jname, name.c_str()); + if (didAttachThread) + { + m_vm->DetachCurrentThread(); + } + + return true; +} + +bool CPhoneDevice::FireTimer(timer_uid_t uid, unsigned long times) { std::map::iterator it = mTimers.find(uid); if (it == mTimers.end()) @@ -306,21 +403,21 @@ bool CPhoneDevice::FireTimer(timer_uid_t uid) } unsigned long timerType = it->second & 0xFFFFFFFF; - unsigned long times = (it->second & 0xFFFFFFFF00000000) >> 32; - times++; + unsigned long ntimes = (it->second & 0xFFFFFFFF00000000) >> 32; + ntimes++; if (timerType != 100) { int aa = 0; } - it->second = timerType | (times << 32); + it->second = timerType | (ntimes << 32); if (m_listener == NULL) { return false; } - m_listener->OnTimeout(uid, timerType, times); + m_listener->OnTimeout(uid, timerType, ntimes); return true; } @@ -332,10 +429,16 @@ IDevice::timer_uid_t CPhoneDevice::RegisterHeartbeat(unsigned int timerType, uns IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1); - JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); - if (attached) + JNIEnv* env = NULL; + bool didAttachThread = false; + bool res = GetJniEnv(m_vm, &env, didAttachThread); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + if (didAttachThread) { env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout); m_vm->DetachCurrentThread(); @@ -346,43 +449,100 @@ IDevice::timer_uid_t CPhoneDevice::RegisterHeartbeat(unsigned int timerType, uns bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector& osds, const string& path) { - LOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset); + if (mCamera != NULL) + { + delete mCamera; + mCamera = NULL; + } + + ALOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset); mPhotoInfo = photoInfo; mPath = path; mOsds = osds; mCamera = new CPhoneCamera(this, photoInfo.width, photoInfo.height); - mCamera->open(to_string(photoInfo.channel - 1).c_str()); + if (mCamera->open(to_string(photoInfo.channel - 1).c_str()) == 0) + { + } + else + { + delete mCamera; + mCamera = NULL; + return false; + } return true; } -void CPhoneDevice::OnImageReady(const cv::Mat& mat) const +bool CPhoneDevice::CloseCamera() { + if (mCamera != NULL) + { + delete mCamera; + mCamera = NULL; + } + return true; +} +bool CPhoneDevice::OnImageReady(const cv::Mat& mat) const +{ int baseline = 0; cv::Size textSize, textSize2; + double fontScale = 1; // base 1024 + double height = mat.size().height; + double width = mat.size().width; + double ratio = std::min(height / 1024, width / 1920); + fontScale = fontScale * ratio; + double thickness1 = 4 * ratio; + double thickness2 = 2 * ratio; 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, 1, 4, &baseline); - cv::Point pt(it->x, it->y + textSize.height); - putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0), 4,cv::LINE_AA); - textSize2 = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, 1, 2, &baseline); + 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, 1, cv::Scalar(255, 255, 255), 2,cv::LINE_AA); + putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, fontScale, cv::Scalar(255, 255, 255), thickness2,cv::LINE_AA); } vector compression_params; compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); - compression_params.push_back(80); + compression_params.push_back(mPhotoInfo.quality); - bool res = cv::imwrite(mPath.c_str(), mat, compression_params); + if (!std::filesystem::exists(std::filesystem::path(mPath))) + { + bool res = cv::imwrite(mPath.c_str(), mat, compression_params); + if (!res) + { + ALOGE("Failed to write photo: %s", mPath.c_str()); + } + else + { + ALOGI("Succeeded to write photo: %s", mPath.c_str()); + } + TakePhotoCb(res, mPhotoInfo, mPath, time(NULL)); + return res; + } + else + { + ALOGI("Photo file exists: %s", mPath.c_str()); + } + // mCamera->close(); + // delete mCamera; + // mCamera = NULL; + return true; +} + +void CPhoneDevice::onError(const std::string& msg) const +{ + // XFLOG(XFLOG_SEVERITY_ERROR, "Failed to Take Photo: %s", msg.c_str()); - TakePhotoCb(res, mPhotoInfo, mPath, time(NULL)); + ALOGE("Failed to Take Photo: %s", msg.c_str()); - delete mCamera; - mCamera = NULL; + // mCamera->close(); + + TakePhotoCb(false, mPhotoInfo, mPath, 0); } std::string CPhoneDevice::GetFileName() const diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index 62bfb2cd..0a828862 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -11,21 +11,17 @@ #include #include +#include #include #include #include #include #include -#include -// #define LOG_TAG "native-camera2-jni" -#define PD_LOG_TAG "PhoneDev" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,PD_LOG_TAG,__VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,PD_LOG_TAG,__VA_ARGS__) #include #include -#include "Camera2Helper.h" +#include "camera2/Camera2Helper.h" #include "camera2/ndkcamera.h" @@ -37,14 +33,18 @@ public: virtual void SetListener(IListener* listener); virtual bool UpdateTime(time_t ts); + virtual bool UpdateSchedules(); virtual bool QuerySystemProperties(map& properties); virtual bool Reboot(); 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 timer_uid_t RegisterTimer(unsigned int timerType, unsigned int timeout); + virtual bool CloseCamera(); + virtual timer_uid_t RegisterTimer(unsigned int timerType, unsigned int timeout, unsigned long times = 0); virtual bool UnregisterTimer(timer_uid_t uid); + virtual unsigned long RequestWakelock(unsigned long timeout); + virtual bool ReleaseWakelock(unsigned long wakelock); - virtual bool FireTimer(timer_uid_t uid); + virtual bool FireTimer(timer_uid_t uid, unsigned long times); bool GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector& items); @@ -65,7 +65,8 @@ protected: return false; } - void OnImageReady(const cv::Mat& mat) const; + bool OnImageReady(const cv::Mat& mat) const; + void onError(const std::string& msg) const; protected: @@ -77,18 +78,23 @@ protected: jmethodID mUnregisterTimerMid; jmethodID mUpdateTimeMid; + jmethodID mRequestWakelockMid; + jmethodID mReleaseWakelockMid; + std::string mPath; IDevice::PHOTO_INFO mPhotoInfo; vector mOsds; IListener* m_listener; atomic_ulong m_timerUidFeed; + atomic_ulong m_wakelockIdFeed; std::map mTimers; class CPhoneCamera : public NdkCamera { public: CPhoneCamera(CPhoneDevice* dev, int32_t width, int32_t height); - virtual void on_image(const cv::Mat& rgb) const; + virtual bool on_image(const cv::Mat& rgb) const; + virtual void on_error(const std::string& msg) const; protected: CPhoneDevice* m_dev; @@ -98,7 +104,6 @@ protected: time_t mHeartbeatStartTime; unsigned int mHeartbeatDuration; - }; diff --git a/app/src/main/cpp/PhoneDevice2.cpp b/app/src/main/cpp/PhoneDevice2.cpp index ee72878b..2cd12887 100644 --- a/app/src/main/cpp/PhoneDevice2.cpp +++ b/app/src/main/cpp/PhoneDevice2.cpp @@ -19,7 +19,6 @@ #define LOG_TAG "CameraTestHelpers" #include "PhoneDevice2.h" -#include "TermClient.h" #include #include @@ -33,15 +32,9 @@ #include -#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#define ASSERT(cond, fmt, ...) \ - if (!(cond)) { \ - __android_log_assert(#cond, LOG_TAG, fmt, ##__VA_ARGS__); \ - } +#include -extern bool GetJniEnv(JavaVM *vm, JNIEnv **env); +extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their @@ -80,7 +73,12 @@ CPhoneDevice2::CPhoneDevice2(JavaVM* vm, jobject service) { m_vm = vm; JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } m_javaService = env->NewGlobalRef(service); jclass classService = env->GetObjectClass(m_javaService); @@ -103,7 +101,12 @@ CPhoneDevice2::CPhoneDevice2(JavaVM* vm, jobject service) CPhoneDevice2::~CPhoneDevice2() { JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } env->DeleteGlobalRef(m_javaService); if (attached) { @@ -121,11 +124,17 @@ bool CPhoneDevice2::UpdateTime(time_t ts) { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return false; + } + jlong timeInMillis = ((jlong)ts) * 1000; + ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); if (attached) { - jlong timeInMillis = ((jlong)ts) * 1000; - ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); m_vm->DetachCurrentThread(); } @@ -145,10 +154,17 @@ IDevice::timer_uid_t CPhoneDevice2::RegisterTimer(unsigned int timerType, unsign JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout); + if (attached) { - ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout); m_vm->DetachCurrentThread(); } @@ -165,10 +181,16 @@ bool CPhoneDevice2::UnregisterTimer(IDevice::timer_uid_t uid) { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return false; + } + ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid); if (attached) { - ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid); m_vm->DetachCurrentThread(); } @@ -214,10 +236,16 @@ IDevice::timer_uid_t CPhoneDevice2::RegisterHeartbeat(unsigned int timerType, un JNIEnv* env = NULL; jboolean ret = JNI_FALSE; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + return 0; + } + env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout); if (attached) { - env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout); m_vm->DetachCurrentThread(); } @@ -226,15 +254,15 @@ IDevice::timer_uid_t CPhoneDevice2::RegisterHeartbeat(unsigned int timerType, un bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector& osds, const string& path) { - LOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset); + ALOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset); mPhotoInfo = photoInfo; mPath = path; mDisplayDimension = DisplayDimension(photoInfo.width, photoInfo.height); - LOGE("Image Buffer Size: %d", photoInfo.width * photoInfo.height * 4); + ALOGE("Image Buffer Size: %d", photoInfo.width * photoInfo.height * 4); imageBuffer_ = (uint8_t*)malloc(photoInfo.width * photoInfo.height * 4); - ASSERT(imageBuffer_ != nullptr, "Failed to allocate imageBuffer_"); + AASSERT(imageBuffer_ != nullptr, "Failed to allocate imageBuffer_"); int cameraId = (int)photoInfo.channel - 1; @@ -247,33 +275,33 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector camera_status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList); if (camera_status != ACAMERA_OK) { - LOGI("Failed to get camera id list (reason: %d)\n", camera_status); + ALOGI("Failed to get camera id list (reason: %d)\n", camera_status); TakePhotoCb(false, photoInfo, path, 0); return false; } if (cameraIdList->numCameras < 1 ) { - LOGI("No camera device detected.\n"); + ALOGI("No camera device detected.\n"); TakePhotoCb(false, photoInfo, path, 0); return false; } if (cameraIdList->numCameras <= cameraId ) { - LOGI("No required camera device %d detected.\n", cameraId); + ALOGI("No required camera device %d detected.\n", cameraId); TakePhotoCb(false, photoInfo, path, 0); return false; } selectedCameraId = cameraIdList->cameraIds[cameraId]; - LOGI("Trying to open Camera2 (id: %s, num of camera : %d)\n", selectedCameraId, + ALOGI("Trying to open Camera2 (id: %s, num of camera : %d)\n", selectedCameraId, cameraIdList->numCameras); camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId, &cameraMetadata); if (camera_status != ACAMERA_OK) { - LOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId); + ALOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId); } ACameraMetadata_const_entry face, orientation; @@ -287,7 +315,7 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector camera_status = ACameraMetadata_getConstEntry(cameraMetadata, ACAMERA_SENSOR_ORIENTATION, &orientation); - LOGI("====Current SENSOR_ORIENTATION: %8d", orientation.data.i32[0]); + ALOGI("====Current SENSOR_ORIENTATION: %8d", orientation.data.i32[0]); uint32_t cameraOrientation_ = orientation.data.i32[0]; if (cameraOrientation_ == 90 || cameraOrientation_ == 270) { @@ -304,14 +332,14 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector &deviceStateCallbacks, &cameraDevice); if (camera_status != ACAMERA_OK) { - LOGI("Failed to open camera device (id: %s)\n", selectedCameraId); + ALOGI("Failed to open camera device (id: %s)\n", selectedCameraId); } camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_STILL_CAPTURE/*TEMPLATE_PREVIEW*/, &captureRequest); if (camera_status != ACAMERA_OK) { - LOGI("Failed to create preview capture request (id: %s)\n", selectedCameraId); + ALOGI("Failed to create preview capture request (id: %s)\n", selectedCameraId); } ACaptureSessionOutputContainer_create(&captureSessionOutputContainer); @@ -329,7 +357,7 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector status = AImageReader_new(resCap.width, resCap.height, resCap.format, 5, &mAImageReader); if (status != AMEDIA_OK) { - LOGI("AImageReader_new error\n"); + ALOGI("AImageReader_new error\n"); TakePhotoCb(false, photoInfo, path, 0); return false; } @@ -344,12 +372,12 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector status = AImageReader_getWindow(mAImageReader, &theNativeWindow); if (status != AMEDIA_OK) { - LOGI("AImageReader_getWindow error\n"); + ALOGI("AImageReader_getWindow error\n"); TakePhotoCb(false, photoInfo, path, 0); return false; } - LOGI("Surface is prepared in %p.\n", theNativeWindow); + ALOGI("Surface is prepared in %p.\n", theNativeWindow); // theNativeWindow ACameraOutputTarget_create(theNativeWindow, &cameraOutputTarget); @@ -363,7 +391,7 @@ bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector // ACameraCaptureSession_setRepeatingRequest(captureSession, NULL, 1, &captureRequest, NULL); ACameraCaptureSession_capture(captureSession, NULL, 1, &captureRequest, NULL); - LOGI("Surface is prepared in here.\n"); + ALOGI("Surface is prepared in here.\n"); return true; } @@ -389,10 +417,10 @@ void CPhoneDevice2::ImageCallback(AImageReader *reader) { int32_t srcFormat = -1; AImage_getFormat(image, &srcFormat); - ASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format"); + AASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format"); int32_t srcPlanes = 0; AImage_getNumberOfPlanes(image, &srcPlanes); - ASSERT(srcPlanes == 3, "Is not 3 planes"); + AASSERT(srcPlanes == 3, "Is not 3 planes"); AImageCropRect srcRect; AImage_getCropRect(image, &srcRect); @@ -475,10 +503,10 @@ bool CPhoneDevice2::WriteFile(AImage *image, const string& path) int planeCount = 0; media_status_t status = AImage_getNumberOfPlanes(image, &planeCount); - LOGI("Info: getNumberOfPlanes() planeCount = %d", planeCount); + ALOGI("Info: getNumberOfPlanes() planeCount = %d", planeCount); if (!(status == AMEDIA_OK && planeCount == 1)) { - LOGE("Error: getNumberOfPlanes() planeCount = %d", planeCount); + ALOGE("Error: getNumberOfPlanes() planeCount = %d", planeCount); return false; } @@ -493,7 +521,7 @@ bool CPhoneDevice2::WriteFile(AImage *image, const string& path) fwrite(data, 1, len, file); fclose(file); - LOGI("Capture: %s", path.c_str()); + ALOGI("Capture: %s", path.c_str()); res = true; @@ -546,7 +574,7 @@ bool CPhoneDevice2::MatchCaptureSizeRequest(ACameraManager *cameraManager, const if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) { DisplayDimension res(entry.data.i32[i + 1], entry.data.i32[i + 2]); - LOGI("Camera Resolution: %d x %d fmt=%d", res.width(), res.height(), format); + ALOGI("Camera Resolution: %d x %d fmt=%d", res.width(), res.height(), format); if (!disp.IsSameRatio(res)) continue; if (format == AIMAGE_FORMAT_YUV_420_888 && res > disp) { foundIt = true; @@ -563,7 +591,7 @@ bool CPhoneDevice2::MatchCaptureSizeRequest(ACameraManager *cameraManager, const resCap->width = foundRes.org_width(); resCap->height = foundRes.org_height(); } else { - LOGI("Did not find any compatible camera resolution, taking 640x480"); + ALOGI("Did not find any compatible camera resolution, taking 640x480"); resCap->width = disp.org_width(); resCap->height = disp.org_height(); // *resCap = *resView; @@ -584,16 +612,16 @@ bool CPhoneDevice2::MatchCaptureSizeRequest(ACameraManager *cameraManager, const * it will be deleted via {@link AImage_delete} */ bool CPhoneDevice2::DisplayImage(ANativeWindow_Buffer *buf, AImage *image) { - ASSERT(buf->format == WINDOW_FORMAT_RGBX_8888 || + AASSERT(buf->format == WINDOW_FORMAT_RGBX_8888 || buf->format == WINDOW_FORMAT_RGBA_8888, "Not supported buffer format"); int32_t srcFormat = -1; AImage_getFormat(image, &srcFormat); - ASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format"); + AASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format"); int32_t srcPlanes = 0; AImage_getNumberOfPlanes(image, &srcPlanes); - ASSERT(srcPlanes == 3, "Is not 3 planes"); + AASSERT(srcPlanes == 3, "Is not 3 planes"); switch (presentRotation_) { case 0: @@ -609,7 +637,7 @@ bool CPhoneDevice2::DisplayImage(ANativeWindow_Buffer *buf, AImage *image) { PresentImage270(buf, image); break; default: - ASSERT(0, "NOT recognized display rotation: %d", presentRotation_); + AASSERT(0, "NOT recognized display rotation: %d", presentRotation_); } AImage_delete(image); @@ -858,27 +886,27 @@ bool CPhoneDevice2::SendBroadcastMessage(String16 action, int value) void CPhoneDevice2::camera_device_on_disconnected(void *context, ACameraDevice *device) { - LOGI("Camera(id: %s) is diconnected.\n", ACameraDevice_getId(device)); + ALOGI("Camera(id: %s) is diconnected.\n", ACameraDevice_getId(device)); CPhoneDevice2* pThis = (CPhoneDevice2*)context; // delete pThis; } void CPhoneDevice2::camera_device_on_error(void *context, ACameraDevice *device, int error) { - LOGI("Error(code: %d) on Camera(id: %s).\n", error, ACameraDevice_getId(device)); + ALOGI("Error(code: %d) on Camera(id: %s).\n", error, ACameraDevice_getId(device)); } void CPhoneDevice2::capture_session_on_ready(void *context, ACameraCaptureSession *session) { - LOGI("Session is ready. %p\n", session); + ALOGI("Session is ready. %p\n", session); } void CPhoneDevice2::capture_session_on_active(void *context, ACameraCaptureSession *session) { - LOGI("Session is activated. %p\n", session); + ALOGI("Session is activated. %p\n", session); } void CPhoneDevice2::capture_session_on_closed(void *context, ACameraCaptureSession *session) { - LOGI("Session is closed. %p\n", session); + ALOGI("Session is closed. %p\n", session); } \ No newline at end of file diff --git a/app/src/main/cpp/PhoneDevice2.h b/app/src/main/cpp/PhoneDevice2.h index 16c2015f..dc9cfe43 100644 --- a/app/src/main/cpp/PhoneDevice2.h +++ b/app/src/main/cpp/PhoneDevice2.h @@ -17,15 +17,10 @@ #include #include #include -#include -// #define LOG_TAG "native-camera2-jni" -#define PD_LOG_TAG "PhoneDev" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,PD_LOG_TAG,__VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,PD_LOG_TAG,__VA_ARGS__) #include #include -#include "Camera2Helper.h" +#include "camera2/Camera2Helper.h" class CPhoneDevice2 : public IDevice { diff --git a/app/src/main/cpp/SystemUsage.h b/app/src/main/cpp/SystemUsage.h new file mode 100644 index 00000000..3497abd2 --- /dev/null +++ b/app/src/main/cpp/SystemUsage.h @@ -0,0 +1,201 @@ +/* --------------------------------------------------------------------- + * Luiz Carlos doleron @ 2021 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero Public License for more details. + * ---------------------------------------------------------------------- + */ + +#ifndef _GET_CPU_USAGE_LINUX_ +#define _GET_CPU_USAGE_LINUX_ + +#include +#include +#include +#include + +namespace get_system_usage_linux +{ + + struct CPU_stats + { + // see http://www.linuxhowtos.org/manpages/5/proc.htm + int user; + int nice; + int system; + int idle; + int iowait; + int irq; + int softirq; + int steal; + int guest; + int guest_nice; + + int get_total_idle() + const { + return idle + iowait; + } + + int get_total_active() + const { + return user + nice + system + irq + softirq + steal + guest + guest_nice; + } + + }; + + struct Memory_stats + { + int total_memory; + int available_memory; + int total_swap; + int free_swap; + + float get_memory_usage() { + const float result = static_cast(total_memory - available_memory) / total_memory; + return result; + } + + float get_swap_usage() { + const float result = static_cast(total_swap - free_swap) / total_swap; + return result; + } + + }; + + inline CPU_stats read_cpu_data() + { + CPU_stats result; + std::ifstream proc_stat("/proc/stat"); + + if (proc_stat.good()) + { + std::string line; + getline(proc_stat, line); + + unsigned int *stats_p = (unsigned int *)&result; + std::stringstream iss(line); + std::string cpu; + iss >> cpu; + while (iss >> *stats_p) + { + stats_p++; + }; + } + + proc_stat.close(); + + return result; + } + + inline int get_val(const std::string &target, const std::string &content) { + int result = -1; + std::size_t start = content.find(target); + if (start != std::string::npos) { + int begin = start + target.length(); + std::size_t end = content.find("kB", start); + std::string substr = content.substr(begin, end - begin); + result = std::stoi(substr); + } + return result; + } + + inline Memory_stats read_memory_data() + { + Memory_stats result; + std::ifstream proc_meminfo("/proc/meminfo"); + + if (proc_meminfo.good()) + { + std::string content((std::istreambuf_iterator(proc_meminfo)), + std::istreambuf_iterator()); + + result.total_memory = get_val("MemTotal:", content); + result.total_swap = get_val("SwapTotal:", content); + result.free_swap = get_val("SwapFree:", content); + result.available_memory = get_val("MemAvailable:", content); + + } + + proc_meminfo.close(); + + return result; + } + + inline float get_cpu_usage(const CPU_stats &first, const CPU_stats &second) { + const float active_time = static_cast(second.get_total_active() - first.get_total_active()); + const float idle_time = static_cast(second.get_total_idle() - first.get_total_idle()); + const float total_time = active_time + idle_time; + return active_time / total_time; + } + + inline float get_disk_usage(const std::string & disk) { + struct statvfs diskData; + + statvfs(disk.c_str(), &diskData); + + auto total = diskData.f_blocks; + auto free = diskData.f_bfree; + auto diff = total - free; + + float result = static_cast(diff) / total; + + return result; + } + + //see https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal/342023 + + inline int find_thermalzone_index() { + int result = 0; + bool stop = false; + // 20 must stop anyway + for (int i = 0; !stop && i < 20; ++i) { + std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(i) + "/type"); + + if (thermal_file.good()) + { + std::string line; + getline(thermal_file, line); + + if (line.compare("x86_pkg_temp") == 0) { + result = i; + stop = true; + } + + } else { + stop = true; + } + + thermal_file.close(); + } + return result; + } + + inline int get_thermalzone_temperature(int thermal_index) { + int result = -1; + std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(thermal_index) + "/temp"); + + if (thermal_file.good()) + { + std::string line; + getline(thermal_file, line); + + std::stringstream iss(line); + iss >> result; + } else { + throw std::invalid_argument(std::to_string(thermal_index) + " doesn't refer to a valid thermal zone."); + } + + thermal_file.close(); + + return result; + } + +} + +#endif diff --git a/app/src/main/cpp/TerminalDevice.cpp b/app/src/main/cpp/TerminalDevice.cpp index 19d91fb2..922aa670 100644 --- a/app/src/main/cpp/TerminalDevice.cpp +++ b/app/src/main/cpp/TerminalDevice.cpp @@ -1,16 +1,22 @@ #include "TerminalDevice.h" #include #include "Camera.h" +#include typedef jbyteArray (*TakePhotoFunc)(int, int, int, int); -extern bool GetJniEnv(JavaVM *vm, JNIEnv **env); +extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); CTerminalDevice::CTerminalDevice(JavaVM* vm, jobject service) { m_vm = vm; JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } m_javaService = env->NewGlobalRef(service); if (attached) { @@ -21,7 +27,12 @@ CTerminalDevice::CTerminalDevice(JavaVM* vm, jobject service) CTerminalDevice::~CTerminalDevice() { JNIEnv* env = NULL; - bool attached = GetJniEnv(m_vm, &env); + bool attached = false; + bool res = GetJniEnv(m_vm, &env, attached); + if (!res) + { + ALOGE("Failed to get JNI Env"); + } env->DeleteGlobalRef(m_javaService); if (attached) { diff --git a/app/src/main/cpp/Camera2Helper.h b/app/src/main/cpp/camera2/Camera2Helper.h similarity index 86% rename from app/src/main/cpp/Camera2Helper.h rename to app/src/main/cpp/camera2/Camera2Helper.h index b3263fdc..d75746eb 100644 --- a/app/src/main/cpp/Camera2Helper.h +++ b/app/src/main/cpp/camera2/Camera2Helper.h @@ -17,6 +17,24 @@ #ifndef __CAMERA2_HELPER_H__ #define __CAMERA2_HELPER_H__ + +template +class RangeValue { +public: + T min_, max_; + /** + * return absolute value from relative value + * value: in percent (50 for 50%) + */ + T value(int percent) { + return static_cast(min_ + (max_ - min_) * percent / 100); + } + RangeValue() { min_ = max_ = static_cast(0); } + + bool Supported(void) const { return (min_ != max_); } +}; + + struct ImageFormat { int32_t width; int32_t height; diff --git a/app/src/main/cpp/camera2/ndkcamera.cpp b/app/src/main/cpp/camera2/ndkcamera.cpp index cebdf602..dfba86e4 100644 --- a/app/src/main/cpp/camera2/ndkcamera.cpp +++ b/app/src/main/cpp/camera2/ndkcamera.cpp @@ -19,6 +19,8 @@ #include #include #include "mat.h" +#include "Camera2Helper.h" +#include static void onDisconnected(void* context, ACameraDevice* device) { @@ -27,7 +29,9 @@ static void onDisconnected(void* context, ACameraDevice* device) static void onError(void* context, ACameraDevice* device, int error) { - __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onError %p %d", device, error); + std::string msg = "NdkCamera error code=" + std::to_string(error); + ((NdkCamera*)context)->on_error(msg); + // __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onError %p %d", device, error); } static void onImageAvailable(void* context, AImageReader* reader) @@ -67,7 +71,7 @@ void onCaptureSequenceAborted(void* context, ACameraCaptureSession* session, int void onCaptureCompleted(void* context, ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result) { - __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureCompleted %p %p %p", session, request, result); + ((NdkCamera*)context)->onCaptureCompleted(session, request, result); } NdkCamera::NdkCamera(int32_t width, int32_t height) @@ -76,6 +80,12 @@ NdkCamera::NdkCamera(int32_t width, int32_t height) camera_orientation = 0; m_firstFrame = true; + mWidth = width; + mHeight = height; + + hdrSupported = false; + nightModeSupported = false; + nightPortraitModeSupported = false; camera_manager = 0; camera_device = 0; @@ -86,23 +96,6 @@ NdkCamera::NdkCamera(int32_t width, int32_t height) capture_session_output_container = 0; capture_session_output = 0; capture_session = 0; - - // setup imagereader and its surface - { - AImageReader_new(width, height, AIMAGE_FORMAT_YUV_420_888, /*maxImages*/2, &image_reader); - - AImageReader_ImageListener listener; - listener.context = this; - listener.onImageAvailable = ::onImageAvailable; - - AImageReader_setImageListener(image_reader, &listener); - - AImageReader_getWindow(image_reader, &image_reader_surface); - - // ANativeWindow_setBuffersGeometry(image_reader_surface, width, height,WINDOW_FORMAT_RGBX_8888); - - ANativeWindow_acquire(image_reader_surface); - } } NdkCamera::~NdkCamera() @@ -124,7 +117,7 @@ NdkCamera::~NdkCamera() int NdkCamera::open(const char* cameraId) { - __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "open"); + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "DBG::open %s", cameraId); // camera_facing = _camera_facing; @@ -132,6 +125,10 @@ int NdkCamera::open(const char* cameraId) // find front camera std::string camera_id; + bool foundIt = false; + DisplayDimension disp(mWidth, mHeight); + DisplayDimension foundRes = disp; + { ACameraIdList* camera_id_list = 0; ACameraManager_getCameraIdList(camera_manager, &camera_id_list); @@ -181,6 +178,95 @@ int NdkCamera::open(const char* cameraId) camera_orientation = orientation; + + { + 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); + // DisplayDimension maxJPG(0, 0); + + for (int i = 0; i < e.count; i += 4) { + int32_t input = e.data.i32[i + 3]; + int32_t format = e.data.i32[i + 0]; + if (input) continue; + + if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) { + DisplayDimension res(e.data.i32[i + 1], e.data.i32[i + 2]); + if (!disp.IsSameRatio(res)) continue; + + if (format == AIMAGE_FORMAT_YUV_420_888 && res > disp) { + foundIt = true; + foundRes = res; + }/* else if (format == AIMAGE_FORMAT_JPEG && res > maxJPG) { + maxJPG = res; + }*/ + } + } + + } + + { + 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) { + exposureRange.min_ = kMinExposureTime; + } + exposureRange.max_ = val.data.i64[1]; + if (exposureRange.max_ > kMaxExposureTime) { + exposureRange.max_ = kMaxExposureTime; + } + // exposureTime = exposureRange.value(2); + } else { + ALOGW("Unsupported ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE"); + exposureRange.min_ = exposureRange.max_ = 0l; + // exposureTime_ = 0l; + } + } + + { + 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]; + sensitivityRange.max_ = val.data.i32[1]; + + // sensitivity = sensitivityRange.value(2); + } else { + ALOGW("failed for ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE"); + sensitivityRange.min_ = sensitivityRange.max_ = 0; + // sensitivity_ = 0; + } + + } + + { + 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]) + { + hdrSupported = true; + break; + } + 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]) + { + nightPortraitModeSupported = true; + } + } + + } + ACameraMetadata_free(camera_metadata); break; @@ -189,11 +275,36 @@ int NdkCamera::open(const char* cameraId) ACameraManager_deleteCameraIdList(camera_id_list); } - if (camera_id.empty()) + if (camera_id.empty() || !foundIt) { return 1; } + if (camera_id == "1") + { + camera_facing = 1; + } + + mCameraId = camera_id; + + camera_status_t res = ACAMERA_OK; + // setup imagereader and its surface + { + AImageReader_new(foundRes.width(), foundRes.height(), AIMAGE_FORMAT_YUV_420_888, /*maxImages*/2, &image_reader); + + AImageReader_ImageListener listener; + listener.context = this; + listener.onImageAvailable = ::onImageAvailable; + + AImageReader_setImageListener(image_reader, &listener); + + AImageReader_getWindow(image_reader, &image_reader_surface); + + // ANativeWindow_setBuffersGeometry(image_reader_surface, width, height,WINDOW_FORMAT_RGBX_8888); + + ANativeWindow_acquire(image_reader_surface); + } + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "open %s %d", camera_id.c_str(), camera_orientation); // open camera @@ -203,16 +314,69 @@ int NdkCamera::open(const char* cameraId) camera_device_state_callbacks.onDisconnected = onDisconnected; camera_device_state_callbacks.onError = onError; - ACameraManager_openCamera(camera_manager, camera_id.c_str(), &camera_device_state_callbacks, &camera_device); + res = ACameraManager_openCamera(camera_manager, camera_id.c_str(), &camera_device_state_callbacks, &camera_device); + if (res != ACAMERA_OK) + { + return 1; + } } std::this_thread::sleep_for(std::chrono::milliseconds(128)); // capture request { - ACameraDevice_createCaptureRequest(camera_device, TEMPLATE_STILL_CAPTURE, &capture_request); + res = ACameraDevice_createCaptureRequest(camera_device, TEMPLATE_STILL_CAPTURE, &capture_request); + + int32_t fpsRange[2] = {10,15}; + res = ACaptureRequest_setEntry_i32(capture_request, ACAMERA_CONTROL_AE_TARGET_FPS_RANGE,2,fpsRange); + + bool usingAE = true; + + if (usingAE) { + uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_ON; + res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_AE_MODE, 1, &aeMode); + // ACaptureRequest_setEntry_i32(capture_request, ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity_); + + + uint8_t aeLockOff = ACAMERA_CONTROL_AE_LOCK_OFF; + 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); + + uint8_t hdrMode = ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10; // ACAMERA_CONTROL_SCENE_MODE_HDR + res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_HDR, 1, &hdrMode); - ACameraOutputTarget_create(image_reader_surface, &image_reader_target); - ACaptureRequest_addTarget(capture_request, image_reader_target); + int32_t sensitivity = sensitivityRange.value(2); + res = ACaptureRequest_setEntry_i32(capture_request,ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity); + int64_t exposureTime = exposureRange.value(5); + res = ACaptureRequest_setEntry_i64(capture_request, ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime); + } + + uint8_t afMode = ACAMERA_CONTROL_AF_MODE_CONTINUOUS_VIDEO; + 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); + + if (hdrSupported) { + uint8_t hdrMode = ACAMERA_CONTROL_SCENE_MODE_HDR; // ACAMERA_CONTROL_SCENE_MODE_HDR + // res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE_HDR, 1, &hdrMode); + res = ACaptureRequest_setEntry_u8(capture_request, ACAMERA_CONTROL_SCENE_MODE, 1, &hdrMode); + + } + + if (nightModeSupported) { + uint8_t modeEnabled = 1; // ACAMERA_CONTROL_SCENE_MODE_HDR + 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 = ACameraOutputTarget_create(image_reader_surface, &image_reader_target); + res = ACaptureRequest_addTarget(capture_request, image_reader_target); } // capture session @@ -223,7 +387,7 @@ int NdkCamera::open(const char* cameraId) camera_capture_session_state_callbacks.onReady = onSessionReady; camera_capture_session_state_callbacks.onClosed = onSessionClosed; - ACaptureSessionOutputContainer_create(&capture_session_output_container); + res = ACaptureSessionOutputContainer_create(&capture_session_output_container); ACaptureSessionOutput_create(image_reader_surface, &capture_session_output); @@ -235,14 +399,14 @@ int NdkCamera::open(const char* cameraId) camera_capture_session_capture_callbacks.context = this; camera_capture_session_capture_callbacks.onCaptureStarted = 0; camera_capture_session_capture_callbacks.onCaptureProgressed = 0; - camera_capture_session_capture_callbacks.onCaptureCompleted = onCaptureCompleted; + camera_capture_session_capture_callbacks.onCaptureCompleted = ::onCaptureCompleted; camera_capture_session_capture_callbacks.onCaptureFailed = onCaptureFailed; camera_capture_session_capture_callbacks.onCaptureSequenceCompleted = onCaptureSequenceCompleted; camera_capture_session_capture_callbacks.onCaptureSequenceAborted = onCaptureSequenceAborted; camera_capture_session_capture_callbacks.onCaptureBufferLost = 0; - ACameraCaptureSession_setRepeatingRequest(capture_session, &camera_capture_session_capture_callbacks, 1, &capture_request, nullptr); - // ACameraCaptureSession_capture(capture_session, &camera_capture_session_capture_callbacks, 1, &capture_request,nullptr); + // ACameraCaptureSession_setRepeatingRequest(capture_session, &camera_capture_session_capture_callbacks, 1, &capture_request, nullptr); + ACameraCaptureSession_capture(capture_session, &camera_capture_session_capture_callbacks, 1, &capture_request,nullptr); } return 0; @@ -250,7 +414,7 @@ int NdkCamera::open(const char* cameraId) void NdkCamera::close() { - __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "close"); + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "DBG::close %s", mCameraId.c_str()); if (capture_session) { @@ -316,7 +480,6 @@ void NdkCamera::onImageAvailable(AImageReader* reader) // return; } - int32_t format; AImage_getFormat(image, &format); @@ -397,12 +560,15 @@ void NdkCamera::onImageAvailable(AImageReader* reader) } AImage_delete(image); +} - ACameraCaptureSession_stopRepeating(capture_session); +void NdkCamera::on_error(const std::string& msg) const +{ } -void NdkCamera::on_image(const cv::Mat& rgb) const +bool NdkCamera::on_image(const cv::Mat& rgb) const { + return false; } void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_height) const @@ -413,7 +579,8 @@ void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_hei int h = 0; int rotate_type = 0; // TODO !!!??? - int co = camera_orientation > 0 ? camera_orientation - 90 : camera_orientation; + // int co = camera_orientation > 0 ? camera_orientation + 90 : camera_orientation; + int co = (camera_orientation + 90) % 360; // int co = 0; if (co == 0) { @@ -464,3 +631,26 @@ void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_hei on_image(rgb); } + +void NdkCamera::onCaptureCompleted(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result) +{ +// CALL_REQUEST(setEntry_i64(requests_[PREVIEW_REQUEST_IDX].request_, + // ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime_)); + + // ACameraMetadata_getConstEntry(result, ) + + ACameraMetadata_const_entry val = { 0 }; + camera_status_t status = ACameraMetadata_getConstEntry(result, ACAMERA_SENSOR_EXPOSURE_TIME, &val); + int64_t exTime = val.data.i64[0]; + + + + val = { 0 }; + status = ACameraMetadata_getConstEntry(result, ACAMERA_CONTROL_AE_MODE, &val); + uint8_t aeMode = val.data.u8[0]; + // ACaptureRequest_setEntry_i32(capture_request, ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity_); + + ALOGD("onCaptureCompleted EXPOSURE_TIME=%lld, camera id=%s, AE=%s", exTime, mCameraId.c_str(), ((aeMode == 1) ? "ON" : "OFF")); + + // __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureCompleted %p %p %p", session, request, result); +} \ No newline at end of file diff --git a/app/src/main/cpp/camera2/ndkcamera.h b/app/src/main/cpp/camera2/ndkcamera.h index 8392ea67..f96acb41 100644 --- a/app/src/main/cpp/camera2/ndkcamera.h +++ b/app/src/main/cpp/camera2/ndkcamera.h @@ -21,10 +21,23 @@ #include #include +#include "Camera2Helper.h" + +/** + * Range of Camera Exposure Time: + * Camera's capability range have a very long range which may be disturbing + * on camera. For this sample purpose, clamp to a range showing visible + * video on preview: 100000ns ~ 250000000ns + */ +static const uint64_t kMinExposureTime = static_cast(1000000); +static const uint64_t kMaxExposureTime = static_cast(250000000); + class NdkCamera { public: + + NdkCamera(int32_t width, int32_t height); virtual ~NdkCamera(); @@ -33,14 +46,32 @@ public: void close(); void onImageAvailable(AImageReader* reader); - virtual void on_image(const cv::Mat& rgb) const; + virtual bool on_image(const cv::Mat& rgb) const; + virtual void on_error(const std::string& msg) const; virtual void on_image(const unsigned char* nv21, int nv21_width, int nv21_height) const; + void onCaptureCompleted(ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result); + + public: int camera_facing; int camera_orientation; bool m_firstFrame; + int32_t mWidth; + int32_t mHeight; + std::string mCameraId; + + bool hdrSupported; + bool nightModeSupported; + bool nightPortraitModeSupported; + bool afSupported; + bool awbSupported; + // int64_t exposureTime_; + RangeValue exposureRange; + // int32_t sensitivity_; + RangeValue sensitivityRange; + private: ACameraManager* camera_manager; diff --git a/app/src/main/cpp/yamc/posix_semaphore.hpp b/app/src/main/cpp/yamc/posix_semaphore.hpp index 4775445f..ca06bdc0 100644 --- a/app/src/main/cpp/yamc/posix_semaphore.hpp +++ b/app/src/main/cpp/yamc/posix_semaphore.hpp @@ -77,7 +77,9 @@ class counting_semaphore { static void throw_errno(const char* what_arg) { +#ifndef __ANDROID__ throw std::system_error(std::error_code(errno, std::generic_category()), what_arg); +#endif } bool do_try_acquirewait(const std::chrono::system_clock::time_point& tp) diff --git a/app/src/main/java/com/xinyingpower/microphoto/DeviceUtil.java b/app/src/main/java/com/xinyingpower/microphoto/DeviceUtil.java new file mode 100644 index 00000000..c548da2e --- /dev/null +++ b/app/src/main/java/com/xinyingpower/microphoto/DeviceUtil.java @@ -0,0 +1,169 @@ +package com.xinyingpower.microphoto; + +import android.app.AlarmManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.telephony.CellLocation; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; +import android.telephony.cdma.CdmaCellLocation; +import android.telephony.gsm.GsmCellLocation; +import android.util.Log; + +public class DeviceUtil { + + + public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; + + public static final int SIGNAL_STRENGTH_POOR = 1; + + public static final int SIGNAL_STRENGTH_MODERATE = 2; + + public static final int SIGNAL_STRENGTH_GOOD = 3; + + public static final int SIGNAL_STRENGTH_GREAT = 4; + + public static final int NUM_SIGNAL_STRENGTH_BINS = 5; + + + + //无 ,低劣的 ,适度的 ,好 ,优异的 + public static final String[] SIGNAL_STRENGTH_NAMES = { + "none", "poor", "moderate", "good", "great" + }; + + public static String getNetworkType(Context context) { + + // ConnectionManager instance + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo ni = connectivityManager.getActiveNetworkInfo(); + + // If not connected, "-" will be displayed + if (ni == null || !ni.isConnected()) return ""; + + // If Connected to Wifi + if (ni.getType() == ConnectivityManager.TYPE_WIFI) return "WIFI"; + + // If Connected to Mobile + String subTypeName = ""; + if (ni.getType() == ConnectivityManager.TYPE_MOBILE) { + int subType = ni.getSubtype(); + switch (subType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + case TelephonyManager.NETWORK_TYPE_GSM: + subTypeName = "2G"; + break; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + subTypeName = "3G"; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + case TelephonyManager.NETWORK_TYPE_IWLAN: + case 19: + subTypeName = "4G"; + break; + case TelephonyManager.NETWORK_TYPE_NR: + subTypeName = "5G"; + break; + default: + subTypeName = ""; + break; + } + } + return subTypeName; + } + + public static void getPhoneState(Context context) { + final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + PhoneStateListener MyPhoneListener = new PhoneStateListener() { + @Override + //获取对应网络的ID,这个方法在这个程序中没什么用处 + public void onCellLocationChanged(CellLocation location) { + if (location instanceof GsmCellLocation) { + int CID = ((GsmCellLocation) location).getCid(); + } else if (location instanceof CdmaCellLocation) { + int ID = ((CdmaCellLocation) location).getBaseStationId(); + } + } + + //系统自带的服务监听器,实时监听网络状态 + @Override + public void onServiceStateChanged(ServiceState serviceState) { + super.onServiceStateChanged(serviceState); + } + + //这个是我们的主角,就是获取对应网络信号强度 + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + //这个ltedbm 是4G信号的值 + String signalinfo = signalStrength.toString(); + String[] parts = signalinfo.split(" "); + String ltedbm = parts[9]; + //这个dbm 是2G和3G信号的值 + int asu = signalStrength.getGsmSignalStrength(); + int dbm = -113 + 2 * asu; + + if (telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) { + Log.i("NetWorkUtil", "网络:LTE 信号强度:" + ltedbm + "======Detail:" + signalinfo); + } else if (telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSDPA || + telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSPA || + telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSUPA || + telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS) { + String bin; + if (dbm > -75) { + bin = "网络很好"; + } else if (dbm > -85) { + bin = "网络不错"; + } else if (dbm > -95) { + bin = "网络还行"; + } else if (dbm > -100) { + bin = "网络很差"; + } else { + bin = "网络错误"; + } + Log.i("NetWorkUtil", "网络:WCDMA 信号值:" + dbm + "========强度:" + bin + "======Detail:" + signalinfo); + } else { + String bin; + if (asu < 0 || asu >= 99) bin = "网络错误"; + else if (asu >= 16) bin = "网络很好"; + else if (asu >= 8) bin = "网络不错"; + else if (asu >= 4) bin = "网络还行"; + else bin = "网络很差"; + Log.i("NetWorkUtil", "网络:GSM 信号值:" + dbm + "========强度:" + bin + "======Detail:" + signalinfo); + } + super.onSignalStrengthsChanged(signalStrength); + } + }; + telephonyManager.listen(MyPhoneListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); + } + + public static boolean updateTime(Context context, long timeInMillis) { + boolean res = false; + try { + // Calendar c = Calendar.getInstance(); + // c.set(2010, 1, 1, 12, 00, 00); + AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + am.setTime(timeInMillis); + res = true; + } catch (Exception ex) { + int aa = 0; + } + return true; + } + +} diff --git a/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java b/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java index 85762f3c..bee385ea 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java +++ b/app/src/main/java/com/xinyingpower/microphoto/FloatingWindow.java @@ -50,9 +50,14 @@ public class FloatingWindow extends Service { @Override public void onDestroy() { - if (mView != null) { - mWindowManager.removeView(mView); + try { + if (mView != null) { + mWindowManager.removeView(mView); + } + } catch (Exception ex) { + ex.printStackTrace(); } + super.onDestroy(); } diff --git a/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java b/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java index 5091454d..e32a80d0 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java +++ b/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java @@ -1,9 +1,11 @@ package com.xinyingpower.microphoto; import android.Manifest; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.Path; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.location.Location; @@ -13,17 +15,16 @@ import android.media.MediaRecorder; import android.os.Build; import android.os.Environment; import android.os.Handler; +import android.os.PowerManager; import android.os.SystemClock; -//import androidx.camera.video.FallbackStrategy; -//import androidx.camera.video.Quality; -//import androidx.camera.video.QualitySelector; import androidx.core.app.ActivityCompat; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.View; @@ -35,17 +36,25 @@ import com.xinyingpower.microphoto.databinding.ActivityMainBinding; //import com.xinyingpower.microphoto.request.INettyMessageListener; //import com.xinyingpower.microphoto.request.NettyChatClient; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; -//import io.netty.util.Constant; +import android.content.SharedPreferences; -public class MainActivity extends AppCompatActivity { +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; private static int MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE = 100; @@ -53,17 +62,25 @@ public class MainActivity extends AppCompatActivity { static { System.loadLibrary("microphoto"); } - private ActivityMainBinding binding; private int defaultDataSubId; -// private MediaRecorder mediaRecorder; - + @Override + protected void onDestroy() { + super.onDestroy(); + } + protected class AppConfig { + public String cmdid; + public String server; + public int port; + public int protocol; + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); + // getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); Display defaultDisplay = windowManager.getDefaultDisplay(); @@ -72,13 +89,27 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "Screen Size: " + width + " x " + height); + String cmdid = "0123456789ABCDEFG"; + String server = "47.96.238.157"; + Integer port = new Integer(6891); + Integer protocol = new Integer(0xFF00); // 0xFF00 + + AppConfig appConfig = getAppConfig(); + binding.cmdid.setText(appConfig.cmdid); + binding.server.setText(appConfig.server); + binding.port.setText(Integer.toString(appConfig.port)); this.binding.startServBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - - String[] accessPermissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.FOREGROUND_SERVICE, Manifest.permission.READ_PHONE_STATE, - /*Manifest.permission.SET_TIME,*/}; + String[] accessPermissions = new String[]{ + Manifest.permission.CAMERA, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.FOREGROUND_SERVICE, + Manifest.permission.READ_PHONE_STATE, + /*Manifest.permission.PACKAGE_USAGE_STATS,*/ + /*Manifest.permission.SET_TIME,*/ + }; boolean needRequire = false; for (String access : accessPermissions) { int curPermission = ActivityCompat.checkSelfPermission(MainActivity.this, access); @@ -88,7 +119,10 @@ public class MainActivity extends AppCompatActivity { } } if (needRequire) { - ActivityCompat.requestPermissions(MainActivity.this, accessPermissions, MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE); + ActivityCompat.requestPermissions( + MainActivity.this, + accessPermissions, + MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE); return; } @@ -97,8 +131,19 @@ public class MainActivity extends AppCompatActivity { String fileName = buildPhotoFileName(channel, 255); // MainActivity.this.takePhoto(channel, 255, path, fileName); + String cmdid = MainActivity.this.binding.cmdid.getText().toString(); + String server = MainActivity.this.binding.server.getText().toString(); + int port = Integer.parseInt(MainActivity.this.binding.port.getText().toString()); + int protocol = 0xFF00; + + MainActivity.this.saveAppConfig(cmdid, server, port, protocol); + Intent intent = new Intent(MainActivity.this, MicroPhotoService.class); intent.setAction(MicroPhotoService.ACTION_START); + intent.putExtra("cmdid", cmdid); + intent.putExtra("server", server); + intent.putExtra("port", port); + intent.putExtra("protocol", protocol); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { MainActivity.this.startForegroundService(intent); @@ -108,25 +153,30 @@ public class MainActivity extends AppCompatActivity { binding.startServBtn.setEnabled(false); binding.stopServBtn.setEnabled(true); - } - - }); - this.binding.takePhotoBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - MicroPhotoService.takePhoto(MainActivity.this, 1, 255, true); -// setDefaultDataSubId(1); + MicroPhotoService.takePhoto(MainActivity.this.getApplicationContext(), 1, 255, true); } }); - this.binding.takePhotoBtn2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MicroPhotoService.takePhoto(MainActivity.this, 2, 255, true); -// setDefaultDataSubId(2); + } + }); + this.binding.takePhotoBtn3.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MicroPhotoService.takePhoto(MainActivity.this, 3, 255, true); + } + }); + this.binding.takePhotoBtn4.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MicroPhotoService.takePhoto(MainActivity.this, 4, 255, true); } }); @@ -142,11 +192,8 @@ public class MainActivity extends AppCompatActivity { binding.startServBtn.setEnabled(true); binding.stopServBtn.setEnabled(false); } - - }); - binding.simchange.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -216,14 +263,19 @@ public class MainActivity extends AppCompatActivity { }); } }); - Handler handler=new Handler(); - Runnable runnable=new Runnable(){ - @Override - public void run() { - binding.startServBtn.performClick(); - } - }; - handler.postDelayed(runnable, 1000); + + if (!TextUtils.isEmpty(appConfig.cmdid) && !TextUtils.isEmpty(appConfig.server) && appConfig.port != 0) { + Handler handler = new Handler(); + Runnable runnable = new Runnable() { + @Override + public void run() { + if (binding.startServBtn.isEnabled()) { + binding.startServBtn.performClick(); + } + } + }; + handler.postDelayed(runnable, 5000); + } binding.tcpudp.setOnClickListener(new View.OnClickListener() { @Override @@ -295,7 +347,81 @@ public class MainActivity extends AppCompatActivity { } catch (Exception e) { Log.e(TAG, "wjz debug setDefaultDataSubId: error is " + e.getMessage()); } + } + + private AppConfig getAppConfig() { + + AppConfig appConfig = new AppConfig(); + + String appPath = MicroPhotoService.buildAppDir(this.getApplicationContext()); + + InputStreamReader inputStreamReader = null; + try { + inputStreamReader = new InputStreamReader(new FileInputStream(new File(appPath + "data/App.json")), "UTF-8"); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + String line; + StringBuilder stringBuilder = new StringBuilder(); + while((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line); + } + bufferedReader.close(); + + JSONObject jsonObject = new JSONObject(stringBuilder.toString()); + appConfig.cmdid = jsonObject.getString("CMDID"); + appConfig.server = jsonObject.getString("Server"); + appConfig.port = jsonObject.getInt("Port"); + appConfig.protocol = jsonObject.getInt("Protocol"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + finally { + if (inputStreamReader != null) { + try { + inputStreamReader.close(); + } catch (Exception ex) { + } + + } + } + + return appConfig; + } + + private void saveAppConfig(String cmdid, String server, int port, int protocol) { + + String appPath = MicroPhotoService.buildAppDir(this.getApplicationContext()); + + OutputStreamWriter outputStreamWriter = null; + try { + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("CMDID", cmdid); + jsonObject.put("Server", server); + jsonObject.put("Port", port); + jsonObject.put("Protocol", protocol); + + outputStreamWriter = new OutputStreamWriter(new FileOutputStream(new File(appPath + "data/App.json")), "UTF-8"); + outputStreamWriter.write(jsonObject.toString()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + finally { + if (outputStreamWriter != null) { + try { + outputStreamWriter.close(); + } catch (Exception ex) { + } + } + } } private int getDefaultDataSubId() { diff --git a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java index 8ab45f07..628e21c0 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java +++ b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java @@ -1,6 +1,5 @@ package com.xinyingpower.microphoto; -import android.Manifest; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationChannel; @@ -11,32 +10,22 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Environment; import android.os.IBinder; +import android.os.PowerManager; import android.os.SystemClock; -import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; -import android.telephony.CellLocation; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.telephony.cdma.CdmaCellLocation; -import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; -import com.dowse.camera.client.DSCameraManager; - import java.io.File; +import java.lang.reflect.Method; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -52,62 +41,45 @@ public class MicroPhotoService extends Service { static { System.loadLibrary("microphoto"); } - - private static String ALARM_EVENT = "com.xinyingpower.mp.MicroPhotoService.AlarmReceiver"; - + private static final String ALARM_EVENT = "com.xinyingpower.mp.MicroPhotoService.AlarmReceiver"; public static final int NOTIFICATION_ID_FOREGROUND_SERVICE = 8466503; - - public static final String ACTION_START = "ACT_START"; public static final String ACTION_STOP = "ACT_STOP"; - public static final String ACTION_MAIN = "ACT_MAIN"; - - private static String ACTION_HEARTBEAT = "ACT_HB"; - private static String ACTION_TAKE_PHOTO = "ACT_TP"; - private static String ACTION_TAKE_PHOTO_MANUALLY = "ACT_TP_M"; - - private static String ACTION_TIMEOUT = "ACT_TIMEOUT"; - - private static String EXTRA_PARAM_CHANNEL = "Channel"; - private static String EXTRA_PARAM_PRESET = "Preset"; - private static String EXTRA_PARAM_PHOTO_OR_VIDEO = "PhotoOrVideo"; - private static String EXTRA_PARAM_SCHEDULES = "Schedules"; - private static String EXTRA_PARAM_SCHEDULE = "Schedule_"; - - private static String EXTRA_PARAM_TIME = "Time"; + private static final String ACTION_HEARTBEAT = "ACT_HB"; + private static final String ACTION_TAKE_PHOTO = "ACT_TP"; + private static final String ACTION_TAKE_PHOTO_MANUALLY = "ACT_TP_M"; + private static final String ACTION_TIMEOUT = "ACT_TIMEOUT"; + private static final String EXTRA_PARAM_CHANNEL = "Channel"; + private static final String EXTRA_PARAM_PRESET = "Preset"; + private static final String EXTRA_PARAM_PHOTO_OR_VIDEO = "PhotoOrVideo"; + private static final String EXTRA_PARAM_SCHEDULES = "Schedules"; + private static final String EXTRA_PARAM_SCHEDULE = "Schedule_"; + private static final String EXTRA_PARAM_TIME = "Time"; // private static String EXTRA_PARAM_FILENAME = "FileName"; - - private static String EXTRA_PARAM_TIMER_UID = "TimerUid"; + private static final String EXTRA_PARAM_TIMER_UID = "TimerUid"; // private static String EXTRA_PARAM_TIMER_TYPE = "TimerType"; - private static String EXTRA_PARAM_TIMEOUT = "Timeout"; - - private final static String FOREGROUND_CHANNEL_ID = "foreground_channel_id"; - - + private static final String EXTRA_PARAM_TIMEOUT = "Timeout"; + private static final String EXTRA_PARAM_TIMES = "Times"; + private static final String EXTRA_PARAM_ELASPED_TIMES = "ElapsedTimes"; + private static final String FOREGROUND_CHANNEL_ID = "foreground_channel_id"; public static class STATE_SERVICE { public static final int CONNECTED = 10; public static final int NOT_CONNECTED = 0; } - - private AlarmManager mAlarmManager; private NotificationManager mNotificationManager; - + private final Map mWakeLocks = new HashMap<>(); private int mHeartbeatDuration = 0; // 5m: 5 * 60 * 1000 private long mNextHeartbeatTime = 0; - - private Map mTimers = new HashMap<>(); + private final Map mTimers = new HashMap<>(); private static int stateService = STATE_SERVICE.NOT_CONNECTED; - public MicroPhotoService() { } - @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } - @Override public void onCreate() { super.onCreate(); @@ -115,9 +87,11 @@ public class MicroPhotoService extends Service { mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); stateService = STATE_SERVICE.NOT_CONNECTED; - getPhoneState(this.getApplicationContext()); + DeviceUtil.getPhoneState(this.getApplicationContext()); alarmReceiver = new AlarmReceiver(this); + screenactionreceiver = new ScreenActionReceiver(); + // 注册广播接受者 IntentFilter intentFilter = new IntentFilter(ACTION_HEARTBEAT); intentFilter.addAction(ACTION_TAKE_PHOTO); @@ -133,69 +107,51 @@ public class MicroPhotoService extends Service { break; } + /* if (aci.getShowIntent().isBroadcast()) { // alarmManager.cancel(aci.getShowIntent()); } - + */ } // alarmManager.cancel(); - boolean res = false; - /* - res = DSCameraManager.getInstace().init(); - ChannelPicParam picParam = new ChannelPicParam(); - picParam.setColor(1); - picParam.setWidth(1920); - picParam.setHeight(1080); - picParam.setCompress_radio(40); - DSCameraManager.getInstace().setPicParam(1, picParam); - */ + // boolean res = false; // Environment.getExternalStoragePublicDirectory(String) - File path = new File(Environment.getExternalStorageDirectory(), "com.xyp.mp/"); - if (!path.exists()) { - path.mkdirs(); - } - // File path = getApplicationContext().getFilesDir(); - String appPath = path.getAbsolutePath(); - Log.i(TAG, "AppPath=" + appPath); - // String ip = "180.166.218.222"; - String ip = "47.96.238.157"; - // int port = 40032; - // String ip = "192.168.50.50"; - int port = 6891; - String cmdid = "XY-ANDROIDSIM-002"; - init(appPath, ip, port, cmdid); // registerHeartbeatTimer(getHeartbeatDuration()); + } + @Override + public void onDestroy() { - Date date = new Date(); - long nowTs = date.getTime() / 1000; - date.setHours(0); - date.setMinutes(0); - date.setSeconds(0); - long startTime = date.getTime() / 1000; - long baseTime = nowTs - startTime; + stateService = STATE_SERVICE.NOT_CONNECTED; - registerCaptureSchedule(startTime, baseTime); + uninit(mHandler); + mHandler = 0; - // registerPhotoTimer(); - } + unregisterReceiver(alarmReceiver); + unregisterReceiver(screenactionreceiver); + for(Map.Entry entry : mWakeLocks.entrySet()) { + entry.getValue().release(); + } + mWakeLocks.clear(); + super.onDestroy(); + } public static class AlarmReceiver extends BroadcastReceiver { - private MicroPhotoService mService; - + public AlarmReceiver() { + mService = null; + } public AlarmReceiver(MicroPhotoService service) { mService = service; } - public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (TextUtils.equals(ACTION_HEARTBEAT, action)) { - Log.i(TAG, "receiver ACTION=" + action); - mService.sendHeartbeat(); + Log.i(TAG, "HB Timer Fired ACTION=" + action); + mService.sendHeartbeat(mService.mHandler); mService.registerHeartbeatTimer(); } else if (TextUtils.equals(ACTION_TAKE_PHOTO, action)) { long ts = intent.getLongExtra(EXTRA_PARAM_TIME, 0); @@ -207,7 +163,8 @@ public class MicroPhotoService extends Service { int channel = (int) ((val & 0xFF0000L) >> 16); int preset = (int) ((val & 0xFF00L) >> 8); - mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), true); + Log.i(TAG, "PhotoTimer Fired: CH=" + channel + " PR=" + preset); + mService.notifyToTakePhoto(mService.mHandler, channel, preset, ts, mService.buildPhotoDir(mService.getApplicationContext(), channel), mService.buildPhotoFileName(channel, preset, ts), true); } } @@ -228,23 +185,34 @@ public class MicroPhotoService extends Service { boolean photoOrVideo = intent.getBooleanExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, true); long ts = System.currentTimeMillis() / 1000; - mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), photoOrVideo); + Log.i(TAG, "Take Photo CH=" + channel + " PR=" + preset + " Mannually"); + mService.notifyToTakePhoto(mService.mHandler, channel, preset, ts, mService.buildPhotoDir(mService.getApplicationContext(), channel), mService.buildPhotoFileName(channel, preset, ts), photoOrVideo); } else if (TextUtils.equals(ACTION_TIMEOUT, action)) { long uid = intent.getLongExtra(EXTRA_PARAM_TIMER_UID, 0); - Log.i(TAG, "Timeout:" + uid); - mService.fireTimeout(uid); + long expectedTimes = intent.getLongExtra(EXTRA_PARAM_TIMES, 0); + long elapsedTimes = intent.getLongExtra(EXTRA_PARAM_ELASPED_TIMES, 0); + elapsedTimes++; + + Log.i(TAG, "Timeout uid=" + uid + " expectedTimes=" + expectedTimes + " Times=" + elapsedTimes); + mService.fireTimeout(mService.mHandler, uid, elapsedTimes); + + intent.putExtra(EXTRA_PARAM_ELASPED_TIMES, elapsedTimes); - int timeout = intent.getIntExtra(EXTRA_PARAM_TIMEOUT, 0); Long uidObj = Long.valueOf(uid); - PendingIntent pendingIntent = mService.mTimers.get(uidObj); + if ((expectedTimes == 0) || (elapsedTimes < expectedTimes)) { + int timeout = intent.getIntExtra(EXTRA_PARAM_TIMEOUT, 0); - if (pendingIntent != null) { - mService.registerTimer(pendingIntent, uid, timeout); + PendingIntent pendingIntent = mService.mTimers.get(uidObj); + + if (pendingIntent != null) { + mService.registerTimer(pendingIntent, uid, timeout); + } + } else { + mService.mTimers.remove(uidObj); } } } } - private void registerHeartbeatTimer(int duration) { int orgHeartbeatDuration = mHeartbeatDuration; mHeartbeatDuration = duration; @@ -253,7 +221,6 @@ public class MicroPhotoService extends Service { } } - private void registerHeartbeatTimer() { // 创建延迟意图 @@ -268,7 +235,7 @@ public class MicroPhotoService extends Service { // alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeout, pendingIntent); } - private void registerPhotoTimer(int channel, int preset, long ts, long timeout, List schedules) { + private static void registerPhotoTimer(Context context, int channel, int preset, long ts, long timeout, List schedules) { // 创建延迟意图 Intent alarmIntent = new Intent(); @@ -277,33 +244,39 @@ public class MicroPhotoService extends Service { alarmIntent.putExtra(EXTRA_PARAM_SCHEDULES, cnt); String channelStr = ""; for (int idx = 0; idx < cnt; idx++) { + long val = schedules.get(idx).longValue(); alarmIntent.putExtra(EXTRA_PARAM_SCHEDULE + idx, schedules.get(idx).longValue()); - channelStr += schedules.get(idx).toString() + " "; + channelStr += "CH=" + ((val & 0XFF0000) >> 16) + "-PR=" + ((val & 0XFF00) >> 8) + " "; } alarmIntent.putExtra(EXTRA_PARAM_TIME, ts); - PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); - AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); + AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); long currentTimeMillis = System.currentTimeMillis(); Date date = new Date(currentTimeMillis + timeout); String dateStr = (String) DateFormat.format("MM-dd kk:mm:ss", date); - Log.d(TAG, "Register Photo Timer: " + dateStr + " currentTimeMillis=" + currentTimeMillis + " timeout=" + timeout + " Channels=" + channelStr); + Log.d(TAG, "PhotoTimer Reg: " + dateStr + " currentTimeMillis=" + currentTimeMillis + " timeout=" + timeout + " Channels=" + channelStr); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); } + private void registerPhotoTimer(int channel, int preset, long ts, long timeout, List schedules) { - // private HashMap mTimers = new HashMap(); + registerPhotoTimer(this.getApplicationContext(), channel, preset, ts, timeout, schedules); + } - public boolean registerTimer(long uid, int timeout) { + // private HashMap mTimers = new HashMap(); + public boolean registerTimer(long uid, int timeout, long times) { // 创建延迟意图 Intent alarmIntent = new Intent(); alarmIntent.setAction(ACTION_TIMEOUT); alarmIntent.putExtra(EXTRA_PARAM_TIMER_UID, uid); alarmIntent.putExtra(EXTRA_PARAM_TIMEOUT, timeout); + alarmIntent.putExtra(EXTRA_PARAM_TIMES, times); + alarmIntent.putExtra(EXTRA_PARAM_ELASPED_TIMES, 0L); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -311,6 +284,28 @@ public class MicroPhotoService extends Service { return registerTimer(pendingIntent, uid, timeout); } + private void setDefaultDataSubId(int subId) { + SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + try { + Method method = subscriptionManager.getClass().getDeclaredMethod("setDefaultDataSubId", int.class); + method.invoke(subscriptionManager, subId); + TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + Method method1 = telephonyManager.getClass().getDeclaredMethod("setDataEnabled", boolean.class); + method1.invoke(telephonyManager, true); + } catch (Exception e) { + Log.e(TAG, "wjz debug setDefaultDataSubId: error is " + e.getMessage()); + } + } + private int getDefaultDataSubId() { + SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + try { + Method method = subscriptionManager.getClass().getDeclaredMethod("getDefaultDataSubscriptionId"); + return (int) method.invoke(subscriptionManager); + } catch (Exception e) { + Log.e(TAG, "wjz debug getDefaultDataSubId: error is " + e.getMessage()); + } + return 0; + } public boolean registerTimer(PendingIntent pendingIntent, long uid, int timeout) { AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); @@ -320,7 +315,6 @@ public class MicroPhotoService extends Service { Log.i(TAG, "RegTimer:" + uid + " timeout=" + timeout); return true; } - public boolean unregisterTimer(long uid) { Long uidObj = Long.valueOf(uid); @@ -336,10 +330,9 @@ public class MicroPhotoService extends Service { return true; } - private boolean registerCaptureSchedule(long startTime, long baseTime) { - long[] photoTimeData = getPhotoTimeData(); + long[] photoTimeData = getPhotoTimeData(mHandler); if (photoTimeData == null) { return false; } @@ -373,9 +366,16 @@ public class MicroPhotoService extends Service { preset = (short) ((val & 0xFF00L) >> 8); schedules.add(Long.valueOf(val)); + + Log.i(TAG, "PhotoTimer Reg: CH=" + channel + " PR=" + preset); } else if (ts > currentTs) { break; } else { + + channel = (short) ((val & 0xFF0000L) >> 16); + preset = (short) ((val & 0xFF00L) >> 8); + + Log.i(TAG, "PhotoTimer Reg: CH=" + channel + " PR=" + preset); schedules.add(Long.valueOf(val)); } } @@ -398,11 +398,14 @@ public class MicroPhotoService extends Service { public static void takePhoto(Context context, int channel, int preset, boolean photoOrVideo) { - Intent intent = new Intent(ACTION_TAKE_PHOTO_MANUALLY); - intent.putExtra(EXTRA_PARAM_CHANNEL, channel); - intent.putExtra(EXTRA_PARAM_PRESET, preset); - intent.putExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, photoOrVideo); - context.sendBroadcast(intent); + List schedules = new ArrayList<>(); + long ts = System.currentTimeMillis() / 1000; + long val = (ts << 24); + val |= ((long)channel << 16); + val |= ((long)preset << 8); + schedules.add(Long.valueOf(val)); + + registerPhotoTimer(context, channel, preset, System.currentTimeMillis() / 1000, 0, schedules); } @Override @@ -420,13 +423,42 @@ public class MicroPhotoService extends Service { Log.d(TAG, "Received user starts foreground intent"); startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification()); - // Start the locker receiver - final ScreenActionReceiver screenactionreceiver = new ScreenActionReceiver(); + connect(); + registerReceiver(screenactionreceiver, screenactionreceiver.getFilter()); - connect(); + + // File path = getApplicationContext().getFilesDir(); + String appPath = buildAppDir(this.getApplicationContext()); + + String ip = intent.getStringExtra("server"); + int port = intent.getIntExtra("port", 0); + String cmdid = intent.getStringExtra("cmdid"); + int protocol = intent.getIntExtra("protocol", 0); + + Log.i(TAG, "AppPath=" + appPath + " Server=" + ip + ":" + port + " cmdid=" + cmdid + " protocol" + protocol); + mHandler = init(appPath, ip, port, cmdid, protocol); + + // Start the locker receiver + + if (mHandler !=0) { + Date date = new Date(); + long nowTs = date.getTime() / 1000; + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + long startTime = date.getTime() / 1000; + long baseTime = nowTs - startTime; + + registerCaptureSchedule(startTime, baseTime); + + } + + // registerPhotoTimer(); break; case ACTION_STOP: + unregisterReceiver(screenactionreceiver); + stopForeground(true); stopSelf(); break; @@ -438,6 +470,31 @@ public class MicroPhotoService extends Service { return START_NOT_STICKY; } + public void requestWakelock(String name, long timeout) { + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); + PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, name); + PowerManager.WakeLock wl2 = null; + synchronized (mWakeLocks) { + wl2 = mWakeLocks.replace(name, wl); + } + if (wl2 != null) { + wl2.release(); + } + if (timeout == 0) wl.acquire(); + else wl.acquire(timeout); + } + + public void releaseWakelock(String name) { + PowerManager.WakeLock wl = null; + synchronized (mWakeLocks) { + wl = mWakeLocks.get(name); + mWakeLocks.remove(name); + } + if (wl != null) { + wl.release(); + } + } + private void connect() { // after 10 seconds its connected new android.os.Handler().postDelayed( @@ -510,188 +567,42 @@ public class MicroPhotoService extends Service { .setContentIntent(pendingIntent); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { - notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); + notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); } return notificationBuilder.build(); } - protected String getNetworkType() { - - // ConnectionManager instance - ConnectivityManager connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo ni = connectivityManager.getActiveNetworkInfo(); - - // If not connected, "-" will be displayed - if (ni == null || !ni.isConnected()) return ""; - - // If Connected to Wifi - if (ni.getType() == ConnectivityManager.TYPE_WIFI) return "WIFI"; - - // If Connected to Mobile - String subTypeName = ""; - if (ni.getType() == ConnectivityManager.TYPE_MOBILE) { - int subType = ni.getSubtype(); - switch (subType) { - case TelephonyManager.NETWORK_TYPE_GPRS: - case TelephonyManager.NETWORK_TYPE_EDGE: - case TelephonyManager.NETWORK_TYPE_CDMA: - case TelephonyManager.NETWORK_TYPE_1xRTT: - case TelephonyManager.NETWORK_TYPE_IDEN: - case TelephonyManager.NETWORK_TYPE_GSM: - subTypeName = "2G"; - break; - case TelephonyManager.NETWORK_TYPE_UMTS: - case TelephonyManager.NETWORK_TYPE_EVDO_0: - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - case TelephonyManager.NETWORK_TYPE_EHRPD: - case TelephonyManager.NETWORK_TYPE_HSPAP: - case TelephonyManager.NETWORK_TYPE_TD_SCDMA: - subTypeName = "3G"; - break; - case TelephonyManager.NETWORK_TYPE_LTE: - case TelephonyManager.NETWORK_TYPE_IWLAN: - case 19: - subTypeName = "4G"; - break; - case TelephonyManager.NETWORK_TYPE_NR: - subTypeName = "5G"; - break; - default: - subTypeName = ""; - break; - } - } - return subTypeName; - } - - public static void getPhoneState(Context context) { - final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - PhoneStateListener MyPhoneListener = new PhoneStateListener() { - @Override - //获取对应网络的ID,这个方法在这个程序中没什么用处 - public void onCellLocationChanged(CellLocation location) { - if (location instanceof GsmCellLocation) { - int CID = ((GsmCellLocation) location).getCid(); - } else if (location instanceof CdmaCellLocation) { - int ID = ((CdmaCellLocation) location).getBaseStationId(); - } - } - - //系统自带的服务监听器,实时监听网络状态 - @Override - public void onServiceStateChanged(ServiceState serviceState) { - super.onServiceStateChanged(serviceState); - } - - //这个是我们的主角,就是获取对应网络信号强度 - @Override - public void onSignalStrengthsChanged(SignalStrength signalStrength) { - //这个ltedbm 是4G信号的值 - String signalinfo = signalStrength.toString(); - String[] parts = signalinfo.split(" "); - String ltedbm = parts[9]; - //这个dbm 是2G和3G信号的值 - int asu = signalStrength.getGsmSignalStrength(); - int dbm = -113 + 2 * asu; - - if (telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) { - Log.i("NetWorkUtil", "网络:LTE 信号强度:" + ltedbm + "======Detail:" + signalinfo); - } else if (telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSDPA || - telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSPA || - telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_HSUPA || - telephonyManager.getNetworkType() == TelephonyManager.NETWORK_TYPE_UMTS) { - String bin; - if (dbm > -75) { - bin = "网络很好"; - } else if (dbm > -85) { - bin = "网络不错"; - } else if (dbm > -95) { - bin = "网络还行"; - } else if (dbm > -100) { - bin = "网络很差"; - } else { - bin = "网络错误"; - } - Log.i("NetWorkUtil", "网络:WCDMA 信号值:" + dbm + "========强度:" + bin + "======Detail:" + signalinfo); - } else { - String bin; - if (asu < 0 || asu >= 99) bin = "网络错误"; - else if (asu >= 16) bin = "网络很好"; - else if (asu >= 8) bin = "网络不错"; - else if (asu >= 4) bin = "网络还行"; - else bin = "网络很差"; - Log.i("NetWorkUtil", "网络:GSM 信号值:" + dbm + "========强度:" + bin + "======Detail:" + signalinfo); - } - super.onSignalStrengthsChanged(signalStrength); - } - }; - telephonyManager.listen(MyPhoneListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); - } - - - /* - public boolean takePhoto(short channel, short preset, String path) { - - boolean res = DSCameraManager.getInstace().takePhoto(path, channel, 0, 0); + public static String buildAppDir(Context contxt) { - if (!res) { + File[] paths = contxt.getExternalFilesDirs(null); - int idx = 0; - do { - res = DSCameraManager.getInstace().isCameraServiceOK(); - if (!res) - { - try { - Thread.sleep(1000L); - } catch (InterruptedException ex) { - // ex.printStackTrace(); - } - } - } while(idx < 6); - - if (DSCameraManager.getInstace().isCameraServiceOK()) { - res = DSCameraManager.getInstace().takePhoto(path, channel, 0, 0); - } + if (paths == null || paths.length == 0) { + return null; } - return res; + File path = paths[0]; + if (!path.exists() && !path.mkdirs()) { + return null; + } + String p = path.getAbsolutePath(); + if (!p.endsWith(File.separator)) { + p += File.separator; + } + return p; } - */ - - @Override - public void onDestroy() { - - stateService = STATE_SERVICE.NOT_CONNECTED; - - uninit(); - DSCameraManager.getInstace().unInit(); - super.onDestroy(); - } + public static String buildPhotoDir(Context contxt, int channel) { + // File path = new File(Environment.getExternalStorageDirectory(), "com.xinyingpower.mp/photos/"); - protected 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; - } catch (Exception ex) { - int aa = 0; + String appDir = buildAppDir(contxt); + if (appDir == null) { + return null; } - return true; - } - public String buildPhotoDir(int channel) { - File path = new File(Environment.getExternalStorageDirectory(), "com.xyp.mp/photos/"); + File path = new File(appDir, "photos/"); if (!path.exists() && !path.mkdirs()) { return null; @@ -711,16 +622,38 @@ public class MicroPhotoService extends Service { } + 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; + } catch (Exception ex) { + int aa = 0; + } + return true; + } + + /* + TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); +// for example value of first element +CellInfoGsm cellInfoGsm = (CellInfoGsm)telephonyManager.getAllCellInfo().get(0); +CellSignalStrengthGsm cellSignalStrengthGsm = cellInfoGsm.getCellSignalStrength(); +cellSignalStrengthGsm.getDbm(); + */ - protected native boolean init(String appPath, String ip, int port, String cmdid); - protected native long getHeartbeatDuration(); - protected native long[] getPhotoTimeData(); - protected native long[] getNextScheduleItem(); - protected native boolean notifyToTakePhoto(int channel, int preset, long scheduleTime, String path, String fileName, boolean sendToCma); - protected native boolean sendHeartbeat(); - protected native boolean fireTimeout(long uid); - protected native boolean uninit(); + protected native long init(String appPath, String ip, int port, String cmdid, int protocol); + 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 sendHeartbeat(long handler); + protected native boolean fireTimeout(long handler, long uid, long times); + protected native boolean uninit(long handler); protected long mHandler = 0; private AlarmReceiver alarmReceiver = null; + private ScreenActionReceiver screenactionreceiver = null; } \ No newline at end of file diff --git a/app/src/main/java/com/xinyingpower/microphoto/ScreenActionReceiver.java b/app/src/main/java/com/xinyingpower/microphoto/ScreenActionReceiver.java index b9a278da..7575058c 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/ScreenActionReceiver.java +++ b/app/src/main/java/com/xinyingpower/microphoto/ScreenActionReceiver.java @@ -15,7 +15,6 @@ public class ScreenActionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - //LOG StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); @@ -34,8 +33,6 @@ public class ScreenActionReceiver extends BroadcastReceiver { //Run the locker context.startService(new Intent(context, FloatingWindow.class)); - - } else if(Intent.ACTION_SCREEN_OFF.equals(action)) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3f038f86..d5b8c90d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,91 +4,156 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:clickable="false" android:orientation="horizontal" tools:context=".MainActivity"> + app:layout_constraintTop_toTopOf="@+id/cmdid" /> + android:text="XY-ANDROIDSIM-001" + app:layout_constraintLeft_toRightOf="@+id/textViewCmdId" + app:layout_constraintTop_toTopOf="parent" /> + + android:layout_marginTop="20dp" + android:text="Server" + app:layout_constraintBottom_toBottomOf="@+id/server" + app:layout_constraintStart_toStartOf="@id/textViewCmdId" + app:layout_constraintTop_toTopOf="@+id/server" /> + android:text="47.96.238.157" + app:layout_constraintStart_toStartOf="@+id/cmdid" + app:layout_constraintTop_toBottomOf="@+id/cmdid" /> + + + +