Explorar o código

1. 修复比例尺识别问题。
2. 设置应用有效期(3个月)。

Ulric hai 2 meses
pai
achega
6dfa8e8121

+ 2 - 2
app/build.gradle.kts

@@ -11,8 +11,8 @@ android {
         applicationId = "com.ys.imageProcess"
         minSdk = 29
         targetSdk = 34
-        versionCode = 1
-        versionName = "1.0"
+        versionCode = 3
+        versionName = "1.2"
 
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
         vectorDrawables {

+ 1 - 0
app/src/main/AndroidManifest.xml

@@ -8,6 +8,7 @@
     <uses-permission
         android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:maxSdkVersion="32" />
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application
         android:allowBackup="true"

+ 44 - 6
app/src/main/java/com/ys/imageProcess/MainActivity.kt

@@ -1,14 +1,12 @@
 package com.ys.imageProcess
 
-import android.content.ContentValues
+import android.app.AlertDialog
 import android.content.ContentValues.*
 import android.content.Context
-import android.graphics.BitmapFactory
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.compose.animation.core.withInfiniteAnimationFrameNanos
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
@@ -16,20 +14,30 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.core.app.ActivityCompat
+import androidx.core.app.ActivityCompat.finishAffinity
 import androidx.navigation.compose.rememberNavController
+import com.ys.imageProcess.access.UsagePermission
 import com.ys.imageProcess.ui.navigation.IPNavHost
 import com.ys.imageProcess.ui.theme.ImageProcTheme
-import com.ys.imageProcess.cv.Process
 import com.ys.imageProcess.data.IPFile
 import com.ys.imageProcess.ocr.OCRModelLoader
-import kotlin.math.log
+import kotlin.system.exitProcess
 
 class MainActivity : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        val valid = UsagePermission.checkDatePermissionLocal()
         setContent {
-            IPApp(context = this)
+            if (valid) {
+                IPApp(context = this)
+            } else {
+                showAlertDialog(this) {
+                    finishAffinity(this)
+                    exitProcess(0) // 确保完全退出
+                }
+            }
         }
 
         IPFile.config(this)
@@ -61,6 +69,36 @@ fun IPApp(
     }
 }
 
+private fun showAlertDialog(context: Context, exit: (() -> Unit)? = null) {
+    // 创建一个新的 AlertDialog.Builder 实例
+    val builder = AlertDialog.Builder(context)
+
+    // 设置对话框的标题
+    builder.setTitle("提示")
+
+    // 设置对话框的消息
+    builder.setMessage("应用已过期请更新。")
+
+    // 添加确定按钮
+    builder.setPositiveButton("确定") { dialog, which ->
+        // 点击确定按钮时的操作
+        dialog.dismiss() // 关闭对话框
+        if (exit != null) {
+            exit()
+        }
+    }
+
+    //    // 添加取消按钮
+    //    builder.setNegativeButton("取消") { dialog, which ->
+    //        // 点击取消按钮时的操作
+    //        dialog.cancel() // 关闭对话框
+    //    }
+
+    // 创建并显示对话框
+    val alertDialog = builder.create()
+    alertDialog.show()
+}
+
 @Preview(showBackground = true, showSystemUi = true, device = "id:pixel_tablet")
 @Composable
 fun ChildrenAppPreview() {

+ 115 - 0
app/src/main/java/com/ys/imageProcess/access/UsagePermission.kt

@@ -0,0 +1,115 @@
+package com.ys.imageProcess.access
+
+import android.content.ContentValues.TAG
+import android.util.Log
+import okhttp3.*
+import org.json.JSONObject
+import java.io.IOException
+import java.time.OffsetDateTime
+import java.time.format.DateTimeFormatter
+import java.time.Duration
+
+object UsagePermission {
+    val client = OkHttpClient()
+
+    private const val START_VALID_DATE_STR: String =
+        "2024-09-24T00:00:00.000000+08:00"
+    private const val END_VALID_DATE_STR: String =
+        "2024-12-24T00:00:00.000000+08:00"
+    private const val NOW_DATE_STR: String =
+        "2024-10-25T00:00:00.000000+08:00"
+
+    // 时间服务器的 URL
+    private const val TIME_SERVER_URL = "https://worldtimeapi.org/api/ip"
+
+    // 异步回调接口
+    interface TimeCallback {
+        fun onSuccess(dateTime: OffsetDateTime)
+        fun onFailure(e: IOException)
+    }
+
+    fun checkDatePermissionLocal(): Boolean {
+        val formatter =
+            DateTimeFormatter.ISO_OFFSET_DATE_TIME
+        val start: OffsetDateTime =
+            OffsetDateTime.parse(START_VALID_DATE_STR, formatter)
+        val end: OffsetDateTime =
+            OffsetDateTime.parse(END_VALID_DATE_STR, formatter)
+        val now: OffsetDateTime = OffsetDateTime.now()
+//        now = OffsetDateTime.parse(NOW_DATE_STR, formatter)
+
+        val startToNow: Duration = Duration.between(start, now)
+        val nowToEnd: Duration = Duration.between(now, end)
+        val valid = !(startToNow.isNegative || nowToEnd.isNegative)
+        Log.i(TAG, "passed: $startToNow, left: $nowToEnd, valid:$valid")
+
+        return valid
+    }
+
+    fun checkDatePermissionOnline(valid: ((Boolean) -> Unit)? = null) {
+        fetchNetworkTime(object : TimeCallback {
+            val formatter =
+                DateTimeFormatter.ISO_OFFSET_DATE_TIME
+            val validDate: OffsetDateTime =
+                OffsetDateTime.parse(END_VALID_DATE_STR, formatter)
+
+            override fun onSuccess(dateTime: OffsetDateTime) {
+                val timeDiff: Duration = Duration.between(dateTime, validDate)
+                Log.i(
+                    TAG,
+                    "Network time: $dateTime, valid time: $validDate"
+                )
+                Log.i(TAG, "valid seconds: ${timeDiff.seconds}")
+            }
+
+            override fun onFailure(e: IOException) {
+                Log.i(TAG, "Failed to fetch network time: ${e.message}")
+            }
+        })
+    }
+
+    // 获取网络时间
+    private fun fetchNetworkTime(callback: TimeCallback) {
+        val request = Request.Builder()
+            .url(TIME_SERVER_URL)
+            .build()
+
+        client.newCall(request).enqueue(object : Callback {
+            override fun onFailure(call: Call, e: IOException) {
+                callback.onFailure(e)
+            }
+
+            override fun onResponse(call: Call, response: Response) {
+                if (response.isSuccessful) {
+                    val responseBody = response.body?.string()
+                    if (responseBody != null) {
+                        try {
+                            // 解析 JSON 响应
+                            val json = JSONObject(responseBody)
+                            val dateTimeString = json.getString("datetime")
+                            // 将时间字符串转换为 Date 对象
+                            val formatter =
+                                DateTimeFormatter.ISO_OFFSET_DATE_TIME
+                            // 解析时间字符串为 OffsetDateTime 对象
+                            val offsetDateTime: OffsetDateTime =
+                                OffsetDateTime.parse(dateTimeString, formatter)
+                            // 回调成功
+                            callback.onSuccess(offsetDateTime)
+                        } catch (e: Exception) {
+                            callback.onFailure(
+                                IOException(
+                                    "Failed to parse date and time",
+                                    e
+                                )
+                            )
+                        }
+                    } else {
+                        callback.onFailure(IOException("Empty response body"))
+                    }
+                } else {
+                    callback.onFailure(IOException("Unexpected code ${response.code}"))
+                }
+            }
+        })
+    }
+}

+ 21 - 9
app/src/main/jni/src/algorithm/BugDivide.cpp

@@ -173,19 +173,30 @@ void BugDivide::calcMidLength(std::vector<std::vector<cv::Point>> &contours) {
         double right_most_distance = -100000;
 
         //遍历虫子轮廓上的每个点
-        for (auto const &contour_point: mid_contour_) {
+        for (auto const &contour_point: full_contour_) {
             //计算到圆心的距离
             double distance = cv::norm(contour_point - point);
             //检查是否在圆内且更新最左侧或最右侧离圆心最近的点
             if (distance < radius) {
-                if (distance < left_most_distance && contour_point.x < point.x) {
-                    leftmost_closest_point = contour_point;
-                    left_most_distance = distance;
+                if (contour_point.x < point.x) {
+                    if (left_most_distance < 0) {
+                        left_most_distance = distance;
+                    }
+                    if (distance < left_most_distance) {
+                        leftmost_closest_point = contour_point;
+                        left_most_distance = distance;
+                    }
                 }
 
-                if (distance < right_most_distance && contour_point.x > point.x) {
-                    rightmost_closest_point = contour_point;
-                    right_most_distance = distance;
+
+                if (contour_point.x > point.x) {
+                    if (right_most_distance < 0) {
+                        right_most_distance = distance;
+                    }
+                    if (distance < right_most_distance) {
+                        rightmost_closest_point = contour_point;
+                        right_most_distance = distance;
+                    }
                 }
             }
         }
@@ -203,6 +214,7 @@ void BugDivide::calcMidLength(std::vector<std::vector<cv::Point>> &contours) {
     if (max_width > 0) {
         width_contour_.push_back(max_width_leftmost_point);
         width_contour_.push_back(max_width_rightmost_point);
+        mid_width_pixels_ = max_width;
     }
 }
 
@@ -211,8 +223,8 @@ void BugDivide::calcPixel2um(const std::vector<std::vector<cv::Point>> &contours
     // 定义标签区域的起始点(x起点,y起点)
     int roi_start_x = std::max(0, label_left_point_.x - 100);
     int roi_start_y = std::max(0, label_left_point_.y - 50);
-    int roi_end_x = std::max(input_mat_.cols, label_left_point_.x + 200);
-    int roi_end_y = std::max(input_mat_.rows, label_left_point_.y + 100);
+    int roi_end_x = std::min(input_mat_.cols, label_left_point_.x + 200);
+    int roi_end_y = std::min(input_mat_.rows, label_left_point_.y + 100);
 
     std::vector<std::vector<cv::Point>> contours_in_roi;
     for (const auto &ctr: contours) {