diff --git a/app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt b/app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt index 0336a49..29aa537 100644 --- a/app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt +++ b/app/src/main/java/com/inspection/camera/ui/camera/CameraScreen.kt @@ -76,6 +76,7 @@ import com.inspection.camera.util.PermissionManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeoutOrNull import java.io.File import java.util.concurrent.Executors @@ -158,17 +159,17 @@ fun CameraScreen( } } - // 获取位置 + // 获取位置(10秒超时) LaunchedEffect(permissionsState.allPermissionsGranted, hasLocationPermission) { isLocationLoading = true if (permissionsState.allPermissionsGranted && hasLocationPermission) { try { - Log.d("CameraScreen", "Getting location...") - locationText = locationHelper.getLocationInfo() - Log.d("CameraScreen", "Location result: $locationText") - if (locationText.isEmpty()) { - locationText = "定位失败" + Log.d("CameraScreen", "Getting location with 10s timeout...") + val result = withTimeoutOrNull(10000) { + locationHelper.getLocationInfo() } + locationText = result ?: "定位失败" + Log.d("CameraScreen", "Location result: $locationText") } catch (e: Exception) { Log.e("CameraScreen", "Location error", e) locationText = "定位失败" @@ -200,6 +201,9 @@ fun CameraScreen( onComplete = { uri -> capturedImages.add(uri) isCapturing = false + }, + onError = { + isCapturing = false } ) } @@ -299,7 +303,7 @@ private fun CameraContent( val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA - try { + try { cameraProvider.unbindAll() cameraProvider.bindToLifecycle( lifecycleOwner, @@ -307,8 +311,10 @@ private fun CameraContent( preview, imageCapture ) + Log.d("CameraScreen", "Camera initialized successfully") } catch (e: Exception) { Log.e("CameraScreen", "Camera binding failed", e) + imageCapture = null } }, ContextCompat.getMainExecutor(context)) @@ -462,24 +468,31 @@ private suspend fun getValidLocationTextForPhoto( manualAddress: String, locationHelper: LocationHelper ): String { + Log.d("CameraScreen", "getValidLocationTextForPhoto called, currentLocationText=$currentLocationText") + // 检查当前定位文本是否有效 val invalidTexts = listOf("正在定位...", "定位失败", "请授予定位权限") val isInvalid = currentLocationText.isBlank() || invalidTexts.contains(currentLocationText) if (!isInvalid) { + Log.d("CameraScreen", "Using current location text: $currentLocationText") return currentLocationText } // 使用手动地址 if (manualAddress.isNotBlank()) { + Log.d("CameraScreen", "Using manual address: $manualAddress") return manualAddress } // 尝试快速获取当前位置(使用缓存) + Log.d("CameraScreen", "Requesting new location...") val location = locationHelper.getCurrentLocation() - return location?.let { + val result = location?.let { "${"%.4f".format(it.latitude)}, ${"%.4f".format(it.longitude)}" } ?: "未知地点" + Log.d("CameraScreen", "Got location result: $result") + return result } private fun capturePhoto( @@ -489,8 +502,10 @@ private fun capturePhoto( watermarkStyle: WatermarkStyle, imageQuality: ImageQuality, locationText: String, - onComplete: (Uri) -> Unit + onComplete: (Uri) -> Unit, + onError: () -> Unit = {} ) { + Log.d("CameraScreen", "capturePhoto called, locationText=$locationText") val photoFile = File( context.cacheDir, "photo_${System.currentTimeMillis()}.jpg" @@ -504,9 +519,17 @@ private fun capturePhoto( executor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { + Log.d("CameraScreen", "Photo saved, adding watermark with locationText=$locationText") val bitmap = BitmapFactory.decodeFile(photoFile.absolutePath) - if (bitmap != null) { + if (bitmap == null) { + Log.e("CameraScreen", "Failed to decode photo file: ${photoFile.absolutePath}") + onError() + return + } + + try { val timeText = ImageProcessor.getCurrentTimeText() + Log.d("CameraScreen", "Adding watermark: timeText=$timeText, locationText=$locationText") val watermarkedBitmap = ImageProcessor.addWatermark( bitmap, timeText, @@ -521,12 +544,22 @@ private fun capturePhoto( bitmap.recycle() watermarkedBitmap.recycle() - uri?.let { onComplete(it) } + if (uri != null) { + onComplete(uri) + } else { + Log.e("CameraScreen", "Failed to save image to gallery") + onError() + } + } catch (e: Exception) { + Log.e("CameraScreen", "Error processing image", e) + bitmap.recycle() + onError() } } override fun onError(exception: ImageCaptureException) { Log.e("CameraScreen", "Photo capture failed", exception) + onError() } } ) diff --git a/app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt b/app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt index 4bf68ce..29f57b1 100644 --- a/app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt +++ b/app/src/main/java/com/inspection/camera/ui/gallery/GalleryScreen.kt @@ -69,6 +69,7 @@ fun GalleryScreen( var selectedImages by remember { mutableStateOf>(emptySet()) } var showDeleteDialog by remember { mutableStateOf(false) } var isSelectionMode by remember { mutableStateOf(false) } + var selectedImageUri by remember { mutableStateOf(null) } // 加载图片 LaunchedEffect(Unit) { @@ -149,6 +150,9 @@ fun GalleryScreen( } else { selectedImages + uri } + } else { + // 非选择模式下,点击打开大图查看器 + selectedImageUri = uri } }, onLongClick = { @@ -189,6 +193,32 @@ fun GalleryScreen( } ) } + + // 大图查看对话框 + if (selectedImageUri != null) { + AlertDialog( + onDismissRequest = { selectedImageUri = null }, + title = { Text("图片预览") }, + text = { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(selectedImageUri) + .crossfade(true) + .build(), + contentDescription = "大图预览", + contentScale = ContentScale.Fit, + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + ) + }, + confirmButton = { + TextButton(onClick = { selectedImageUri = null }) { + Text("关闭") + } + } + ) + } } @Composable diff --git a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt index b027841..c5238d4 100644 --- a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt +++ b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt @@ -33,6 +33,44 @@ object ImageProcessor { private val dateFormat = SimpleDateFormat("yyyy年-MM月-dd日 HH:mm:ss", Locale.getDefault()) private val fileNameFormat = SimpleDateFormat("yyyyMMddHHmm", Locale.getDefault()) + /** + * 尝试加载图片 Bitmap + */ + private fun tryLoadBitmap(context: Context, imageItem: ImageItem): Bitmap? { + android.util.Log.d("ImageProcessor", "Loading image, URI: ${imageItem.uri}, path: ${imageItem.path}") + + // 优先尝试 URI 加载 + try { + val uri = imageItem.uri + android.util.Log.d("ImageProcessor", "Trying to open URI: $uri") + context.contentResolver.openInputStream(uri)?.use { inputStream -> + val bitmap = BitmapFactory.decodeStream(inputStream) + android.util.Log.d("ImageProcessor", "URI loaded bitmap: ${bitmap != null}") + return bitmap + } + } catch (e: Exception) { + android.util.Log.e("ImageProcessor", "URI load failed: ${e.message}") + } + + // 尝试从文件路径加载 + if (imageItem.path.isNotEmpty()) { + try { + val file = java.io.File(imageItem.path) + android.util.Log.d("ImageProcessor", "Trying file: ${file.absolutePath}, exists: ${file.exists()}") + if (file.exists()) { + val bitmap = BitmapFactory.decodeFile(imageItem.path) + android.util.Log.d("ImageProcessor", "File loaded bitmap: ${bitmap != null}") + return bitmap + } + } catch (e: Exception) { + android.util.Log.e("ImageProcessor", "File load failed: ${e.message}") + } + } + + android.util.Log.w("ImageProcessor", "Failed to load image") + return null + } + /** * 获取当前时间戳文本 */ @@ -101,9 +139,10 @@ object ImageProcessor { } /** - * 合成多张图片 + * 合成多张图片(支持 URI) */ fun mergeImages( + context: Context, images: List, layoutType: MergeLayoutType, quality: ImageQuality @@ -114,7 +153,6 @@ object ImageProcessor { val cols = layoutType.cols val rows = layoutType.rows - val imageCount = images.size.coerceAtMost(rows * cols) val outputWidth = 1920 val outputHeight = 1080 @@ -137,10 +175,10 @@ object ImageProcessor { val top = row * cellHeight try { - val sourceBitmap = BitmapFactory.decodeFile(imageItem.path) - ?: return@forEachIndexed + val sourceBitmap = tryLoadBitmap(context, imageItem) + + sourceBitmap ?: return@forEachIndexed - // 缩放并居中裁剪 val scaledBitmap = scaleAndCropBitmap(sourceBitmap, cellWidth, cellHeight) val dstRect = Rect(left, top, left + cellWidth, top + cellHeight) canvas.drawBitmap(scaledBitmap, null, dstRect, paint) @@ -150,7 +188,6 @@ object ImageProcessor { } sourceBitmap.recycle() } catch (e: Exception) { - // 加载失败绘制占位 val placeholderPaint = Paint().apply { color = Color.LTGRAY } diff --git a/test_android.py b/test_android.py new file mode 100644 index 0000000..834a473 --- /dev/null +++ b/test_android.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +""" +Android 模拟器图像识别测试脚本 +""" +from airtest.core.api import * +import sys + +# 初始化设备连接 +def init_device(): + # 连接 Android 模拟器 + dev = connect_device("android://127.0.0.1:5037/emulator-5554") + print(f"已连接设备: {dev}") + return dev + +# 截取当前屏幕 +def capture_screen(filename="screen.png"): + dev = device() + screen = dev.snapshot() + + # numpy.ndarray 转换为 PIL Image 并保存 + from PIL import Image + import numpy as np + + img = Image.fromarray(screen) + img.save(filename) + print(f"截图已保存: {filename}") + return screen + +# 测试1: 打开相机应用 +def test_open_camera(): + print("测试1: 打开相机应用...") + + # 使用 ADB 启动相机 + shell("am start -n com.inspection.camera/.ui.MainActivity") + sleep(2) + + # 截图 + capture_screen("test_open_camera.png") + print("相机已打开") + return True + +# 测试2: 模拟点击屏幕中心(测试触摸功能) +def test_touch(): + print("测试2: 测试触摸功能...") + + # 点击屏幕中心 + touch([540, 960]) # 模拟器分辨率 1080x1920 的中心点 + sleep(1) + + capture_screen("test_touch.png") + print("触摸测试完成") + return True + +# 测试3: 滑动测试 +def test_swipe(): + print("测试3: 测试滑动功能...") + + # 从上往下滑动 + swipe([540, 300], [540, 900]) + sleep(1) + + capture_screen("test_swipe.png") + print("滑动测试完成") + return True + +# 测试4: 查找屏幕上是否有特定文字(使用OCR) +def test_ocr(): + print("测试4: OCR文字识别测试...") + + try: + from airtest.aircv import aircv + import numpy as np + + # 截图 + screen = device().snapshot() + + # 使用 PIL 显示图像信息 + from PIL import Image + img = Image.fromarray(screen) + print(f"屏幕分辨率: {img.size}") + print(f"屏幕模式: {img.mode}") + + capture_screen("test_ocr.png") + print("OCR 测试完成") + return True + except Exception as e: + print(f"OCR测试出错: {e}") + return False + +# 主测试函数 +def main(): + print("=" * 50) + print("Android 模拟器图像识别测试") + print("=" * 50) + + try: + # 初始化设备 + init_device() + + # 执行测试 + test_open_camera() + test_touch() + test_swipe() + test_ocr() + + print("\n" + "=" * 50) + print("所有测试完成!") + print("=" * 50) + + except Exception as e: + print(f"测试出错: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() diff --git a/test_camera.py b/test_camera.py new file mode 100644 index 0000000..fb5ec10 --- /dev/null +++ b/test_camera.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +import sys +import io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + +""" +测试拍照功能 +""" +from airtest.core.api import * +from PIL import Image +import os +import time + +def init_device(): + # dev = connect_device("android://127.0.0.1:5037/emulator-5554") # 模拟器 + dev = connect_device("android://127.0.0.1:5037/APH0219A29002701") # 真机 + print(f"已连接设备: {dev}") + return dev + +def capture_screen(filename="screen.png"): + screen = device().snapshot() + img = Image.fromarray(screen) + img.save(filename) + print(f"截图已保存: {filename}") + return screen + +def test_camera(): + print("=" * 50) + print("测试:拍照功能") + print("=" * 50) + + # 启动相机应用 + print("1. 启动相机应用...") + shell("am start -n com.inspection.camera/.ui.MainActivity") + sleep(3) + + capture_screen("01_app_loaded.png") + print(" 相机应用已启动") + + # 点击拍照按钮 (屏幕底部中央 - 2400高,底部区域约在2200) + print("2. 点击拍照按钮...") + touch([540, 2200]) # 拍照按钮位置 + sleep(3) + + capture_screen("02_after_capture.png") + print(" 拍照完成") + + # 检查照片是否生成 + print("3. 检查照片是否生成...") + + try: + # 创建Pictures目录(如果不存在) + shell("mkdir -p /storage/emulated/0/Pictures/inspection") + sleep(1) + + # 列出Pictures目录下的文件 + result = shell("ls -la /storage/emulated/0/Pictures/inspection/") + print(f" Pictures目录内容: {result}") + + # 检查是否有新的照片文件 + files_output = shell("ls /storage/emulated/0/Pictures/inspection/").strip() + + if files_output and "No such file" not in files_output: + files = files_output.split('\n') + print(f" 照片文件列表: {files}") + + if files and files[0]: + latest_photo = files[-1].strip() + print(f" [OK] 照片已生成: {latest_photo}") + + # 拉取最新照片到本地 + pull_cmd = f"/storage/emulated/0/Pictures/inspection/{latest_photo}" + os.system(f'"C:\\Users\\xiaji\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe" -s emulator-5554 pull "{pull_cmd}" . 2>nul') + + if os.path.exists(latest_photo): + print(f" [OK] 照片已保存到本地: {latest_photo}") + print("\n" + "=" * 50) + print("测试结果: 拍照功能正常!") + print("=" * 50) + return True + else: + print(" [X] 照片未保存到本地") + return False + else: + print(" [X] 未找到照片文件") + return False + else: + # 检查DCIM目录 + dcim_result = shell("ls /storage/emulated/0/DCIM/Camera/") + print(f" DCIM目录内容: {dcim_result}") + if dcim_result and "No such file" not in dcim_result and dcim_result.strip(): + print(f" [OK] 照片已生成在DCIM目录") + print("\n" + "=" * 50) + print("测试结果: 拍照功能正常!") + print("=" * 50) + return True + + print(" [X] 未找到任何照片") + return False + + except Exception as e: + print(f" [X] 检查照片时出错: {e}") + return False + +if __name__ == "__main__": + try: + init_device() + result = test_camera() + if result: + print("\n测试通过!") + else: + print("\n测试失败!") + except Exception as e: + print(f"测试出错: {e}") + import traceback + traceback.print_exc() diff --git a/test_gallery.py b/test_gallery.py new file mode 100644 index 0000000..0e10fb8 --- /dev/null +++ b/test_gallery.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +Gallery Test - Test if clicking thumbnail opens full image +""" +from airtest.core.api import * +from PIL import Image +import numpy as np + +def init_device(): + dev = connect_device("android://127.0.0.1:5037/emulator-5554") + print(f"Device connected") + return dev + +def capture_screen(filename="screen.png"): + screen = device().snapshot() + img = Image.fromarray(screen) + img.save(filename) + print(f"Screenshot: {filename}") + return screen + +# Main +print("=" * 50) +print("Gallery Test - Click Thumbnail") +print("=" * 50) + +init_device() +sleep(2) + +# Go to gallery tab +touch([270, 2300]) # Gallery tab +sleep(3) +capture_screen("gallery1_main.png") + +# Click on first thumbnail (latest image) +touch([540, 600]) +sleep(2) +capture_screen("gallery2_after_click.png") + +# Check if screen changed (full image viewer opened) +# Compare two screenshots +img1 = np.array(Image.open("gallery1_main.png")) +img2 = np.array(Image.open("gallery2_after_click.png")) + +# Calculate similarity +diff = np.abs(img1.astype(float) - img2.astype(float)).mean() +print(f"Screen difference: {diff:.2f}") + +# Check if there's any UI change +# If diff > 50, it means screen changed significantly +if diff > 50: + print("RESULT: Screen changed after clicking thumbnail") + print(" (Full image viewer may have opened)") +else: + print("RESULT: Screen did NOT change after clicking thumbnail") + print(" (Click is NOT working - BUG!)") + +print("=" * 50) diff --git a/test_gallery_thumbnail.py b/test_gallery_thumbnail.py new file mode 100644 index 0000000..92ef537 --- /dev/null +++ b/test_gallery_thumbnail.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +""" +Gallery Test - Test clicking thumbnail to view full image +""" +from airtest.core.api import * +from PIL import Image +import numpy as np + +def init_device(): + dev = connect_device("android://127.0.0.1:5037/emulator-5554") + print(f"Device connected") + return dev + +def capture_screen(filename="screen.png"): + screen = device().snapshot() + img = Image.fromarray(screen) + img.save(filename) + print(f"Screenshot: {filename}") + return screen + +def calculate_similarity(img1_path, img2_path): + img1 = np.array(Image.open(img1_path)) + img2 = np.array(Image.open(img2_path)) + diff = np.abs(img1.astype(float) - img2.astype(float)).mean() + return diff + +print("=" * 50) +print("Gallery Test - Click Thumbnail to View Full Image") +print("=" * 50) + +init_device() +sleep(2) + +# Step 1: Open Gallery app +print("Step 1: Open Gallery app") +touch([270, 2300]) # Gallery tab +sleep(3) +capture_screen("gallery_home.png") + +# Step 2: Verify thumbnails are displayed +print("Step 2: Verify thumbnails are displayed") +thumbnails_visible = exists(Template(r"tpl_thumbnail.png")) +print(f"Thumbnails visible: {thumbnails_visible}") + +# Step 3: Click on a thumbnail to view full image +print("Step 3: Click on first thumbnail") +touch([540, 600]) # First thumbnail position +sleep(2) +capture_screen("full_image.png") + +# Step 4: Verify full image viewer opened +print("Step 4: Verify full image viewer opened") +diff = calculate_similarity("gallery_home.png", "full_image.png") +print(f"Screen difference: {diff:.2f}") + +# Step 5: Test result +print("=" * 50) +if diff > 50: + print("PASS: Clicking thumbnail opens full image viewer") +else: + print("FAIL: Clicking thumbnail does NOT open full image") + print(" (This is a BUG - thumbnail click is not working)") +print("=" * 50) diff --git a/test_location_watermark.py b/test_location_watermark.py new file mode 100644 index 0000000..defc909 --- /dev/null +++ b/test_location_watermark.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +from airtest.core.api import * +from poco.drivers.android.uiautomation import AndroidUiautomationPoco +import time + +# 初始化设备 +init_device("Android", uuid="emulator-5554") +poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False) + +print("=" * 50) +print("开始测试拍照功能 - 位置水印测试") +print("=" * 50) + +# 启动应用 +print("\n[1] 启动应用...") +home() +start_app("com.inspection.camera") +sleep(5) + +# 等待应用加载 +print("[2] 等待应用加载...") +snapshot("app_loaded.png") +print("[INFO] 已保存截图: app_loaded.png") + +# 等待定位完成,检测定位文本 +print("\n[3] 等待定位完成(最多15秒)...") +location_text = "" +for i in range(15): + try: + # 尝试通过poco获取定位文本(UI中的卡片文本) + # 定位文本可能位于某个Card内,我们尝试查找包含"定位"的文本元素 + elements = poco(textMatches=".*定位.*") + if elements.exists(): + location_text = elements.get_text() + print(f"[INFO] 检测到定位文本: {location_text}") + if location_text != "正在定位...": + break + except Exception as e: + pass + sleep(1) + print(f"等待 {i+1}/15 秒") + +# 如果未找到定位文本,尝试其他选择器 +if not location_text: + try: + # 尝试查找包含"定位"或"位置"的文本 + for text in ["定位", "位置", "地点"]: + elements = poco(textMatches=f".*{text}.*") + if elements.exists(): + location_text = elements.get_text() + print(f"[INFO] 通过关键词 '{text}' 找到定位文本: {location_text}") + break + except Exception as e: + print(f"[WARNING] 查找定位文本失败: {e}") + +print(f"\n[INFO] 最终定位文本: '{location_text}'") + +# 验证定位是否失败(超时后应显示"定位失败") +if location_text == "定位失败": + print("[SUCCESS] 定位超时后正确显示'定位失败'") +else: + print(f"[WARNING] 定位文本不是'定位失败',而是: {location_text}") + +# 点击拍照按钮(屏幕中央下方) +print("\n[4] 点击拍照按钮...") +shell("input tap 540 2100") +sleep(3) +snapshot("after_capture.png") +print("[INFO] 拍照后截图: after_capture.png") + +# 检查保存的图片文件 +print("\n[4.5] 检查保存的图片文件...") +sleep(2) # 等待文件保存 +# 列出图片目录 +output = shell("ls -t /sdcard/Pictures/InspectionCamera/*.jpg 2>/dev/null | head -1") +if output and output.strip(): + latest_image = output.strip() + print(f"[INFO] 找到最新图片: {latest_image}") + # 获取文件大小 + size_output = shell(f"du -h {latest_image} 2>/dev/null | cut -f1") + if size_output: + print(f"[INFO] 文件大小: {size_output.strip()}") +else: + print("[WARNING] 未找到保存的图片文件,可能路径不同") + # 尝试其他可能路径 + shell("ls -l /sdcard/Pictures/ 2>/dev/null") + shell("ls -l /sdcard/DCIM/ 2>/dev/null") + +# 打开相册 +print("\n[5] 打开相册...") +shell("am start -n com.google.android.apps.photos/.home.HomeActivity") +sleep(3) +snapshot("gallery.png") +print("[INFO] 相册截图: gallery.png") + +# 点击最新图片(假设第一张缩略图位于屏幕中央) +print("\n[6] 点击最新图片...") +touch([540, 600]) +sleep(2) +snapshot("gallery_detail.png") +print("[INFO] 图片详情截图: gallery_detail.png") + +print("\n[7] 测试完成,请检查截图文件中的水印") +print("\n" + "=" * 50) +print("测试完成!") +print("=" * 50) +print("\n请检查截图文件:") +print("1. app_loaded.png - 应用界面") +print("2. after_capture.png - 拍照后") +print("3. gallery.png - 相册") +print("4. gallery_detail.png - 图片详情") +print("\n检查照片左下角是否有时间+位置水印") +print("=" * 50) diff --git a/test_puzzle.py b/test_puzzle.py new file mode 100644 index 0000000..3bbead6 --- /dev/null +++ b/test_puzzle.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- +""" +Puzzle/Merge Function Test Script +Test flow: +1. Click "Puzzle" in bottom navigation +2. Select 2x2 layout +3. Select 4 images +4. Click "Preview" +5. Confirm merge result +6. Return to main page +7. Enter gallery +8. Check latest merge result +""" +from airtest.core.api import * +from PIL import Image +import numpy as np +import os + +# Initialize device +def init_device(): + dev = connect_device("android://127.0.0.1:5037/emulator-5554") + print(f"Device connected: {dev}") + return dev + +# Capture screen +def capture_screen(filename="screen.png"): + screen = device().snapshot() + img = Image.fromarray(screen) + img.save(filename) + print(f"Screenshot saved: {filename}") + return screen + +# Wait for app launch +def wait_app_launch(): + print("Waiting for app launch...") + sleep(3) + +# Test 1: Click "Puzzle" tab in bottom navigation +def click_merge_tab(): + print("Test 1: Click 'Puzzle' tab...") + # Bottom navigation: Camera(0), Gallery(1), Puzzle(2), Settings(3) + # Click puzzle button at bottom right + touch([810, 2300]) + sleep(2) + capture_screen("01_click_merge.png") + print("Clicked puzzle") + return True + +# Test 2: Select 2x2 layout +def select_2x2_layout(): + print("Test 2: Select 2x2 layout...") + touch([180, 200]) + sleep(1) + capture_screen("02_select_2x2.png") + print("Selected 2x2 layout") + return True + +# Test 3: Select 4 images +def select_4_images(): + print("Test 3: Select 4 images...") + + # Click add image button (first empty cell) + touch([270, 500]) + sleep(2) + + # This will open system image picker + capture_screen("03_before_select_image.png") + + # Click "Gallery" option + touch([540, 800]) + sleep(1) + + # Select first image + touch([200, 400]) + sleep(1) + + # Second image + touch([540, 400]) + sleep(1) + + # Third image + touch([880, 400]) + sleep(1) + + # Fourth image + touch([200, 700]) + sleep(1) + + # Confirm selection (click OK button) + touch([900, 2300]) + sleep(2) + + capture_screen("04_selected_4_images.png") + print("Selected 4 images") + return True + +# Test 4: Click preview button +def click_preview_button(): + print("Test 4: Click preview button...") + touch([300, 2200]) + sleep(3) + capture_screen("05_preview.png") + print("Clicked preview") + return True + +# Test 5: Close preview dialog +def close_preview(): + print("Test 5: Close preview dialog...") + touch([540, 1500]) + sleep(1) + capture_screen("06_preview_closed.png") + print("Closed preview") + return True + +# Test 6: Click save button +def click_save_button(): + print("Test 6: Click save button...") + touch([800, 2200]) + sleep(2) + capture_screen("07_save_dialog.png") + return True + +# Test 7: Confirm save +def confirm_save(): + print("Test 7: Confirm save...") + touch([700, 1400]) + sleep(3) + capture_screen("08_saved.png") + print("Saved") + return True + +# Test 8: Return to home page +def go_back_home(): + print("Test 8: Return to home page...") + touch([100, 100]) + sleep(2) + capture_screen("09_back_home.png") + print("Returned to home") + return True + +# Test 9: Click "Gallery" tab +def click_gallery_tab(): + print("Test 9: Click 'Gallery' tab...") + touch([270, 2300]) + sleep(3) + capture_screen("10_gallery.png") + print("Clicked gallery") + return True + +# Test 10: View latest image +def view_latest_image(): + print("Test 10: View latest image...") + touch([540, 600]) + sleep(2) + capture_screen("11_latest_image.png") + print("Opened latest image") + return True + +# 测试11: 检查图片是否为纯白色 +def check_white_image(): + print("Test 11: Check if image is pure white...") + + # 读取截图 + img = Image.open("11_latest_image.png") + img_array = np.array(img) + + # 检查是否为纯白色 (RGB都接近255) + # 取图片中心区域进行判断 + h, w = img_array.shape[:2] + center_region = img_array[h//4:3*h//4, w//4:3*w//4] + + # 计算平均颜色 + avg_color = np.mean(center_region, axis=(0, 1)) + print(f"Image center average color: {avg_color}") + + # 判断是否接近白色 (R, G, B 都 > 250) + is_white = all(c > 250 for c in avg_color[:3]) + + if is_white: + print("WARNING: Image is pure white, merge function may not work!") + else: + print("OK: Image is not pure white, merge function works") + + return not is_white + +# Main test function +def main(): + print("=" * 60) + print("Puzzle/Merge Function Automated Test") + print("=" * 60) + + try: + # Initialize device + init_device() + + # Wait for app launch + wait_app_launch() + + # Execute test steps + click_merge_tab() + select_2x2_layout() + select_4_images() + click_preview_button() + close_preview() + click_save_button() + confirm_save() + go_back_home() + click_gallery_tab() + view_latest_image() + + # Check result + result = check_white_image() + + print("\n" + "=" * 60) + if result: + print("TEST PASSED! Merge function works normally") + else: + print("TEST FAILED! Merge result is pure white") + print("=" * 60) + + except Exception as e: + print(f"Test error: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() diff --git a/test_simple.py b/test_simple.py new file mode 100644 index 0000000..a535a98 --- /dev/null +++ b/test_simple.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +Simple test to verify merge works +""" +from airtest.core.api import * +from PIL import Image +import numpy as np + +def init_device(): + dev = connect_device("android://127.0.0.1:5037/emulator-5554") + print(f"Device connected") + return dev + +def capture_screen(filename="screen.png"): + screen = device().snapshot() + img = Image.fromarray(screen) + img.save(filename) + print(f"Screenshot: {filename}") + return screen + +# Main +print("=" * 50) +print("Simple Merge Test") +print("=" * 50) + +init_device() +sleep(2) + +# Go to merge screen +touch([810, 2300]) # Puzzle tab +sleep(2) +capture_screen("step1_merge.png") + +# Select 2x2 +touch([180, 200]) +sleep(1) + +# Add images +touch([270, 500]) +sleep(2) + +# Select 4 images from gallery +touch([540, 800]) # Gallery tab in picker +sleep(1) +touch([200, 400]) # Image 1 +sleep(0.5) +touch([540, 400]) # Image 2 +sleep(0.5) +touch([880, 400]) # Image 3 +sleep(0.5) +touch([200, 700]) # Image 4 +sleep(0.5) +touch([900, 2300]) # Confirm +sleep(2) + +capture_screen("step2_selected.png") + +# Click preview +touch([300, 2200]) # Preview button +sleep(3) + +capture_screen("step3_preview.png") + +# Check preview result +img = Image.open("step3_preview.png") +img_array = np.array(img) +h, w = img_array.shape[:2] +center_region = img_array[h//4:3*h//4, w//4:3*w//4] +avg_color = np.mean(center_region, axis=(0, 1)) +print(f"Preview average color: {avg_color}") + +# Close preview +touch([540, 1500]) +sleep(1) + +# Save +touch([800, 2200]) # Save button +sleep(2) +touch([700, 1400]) # Confirm save +sleep(3) + +capture_screen("step4_saved.png") + +print("\n" + "=" * 50) +if avg_color[0] < 250 and avg_color[1] < 250 and avg_color[2] < 250: + print("RESULT: Merge works! Preview shows merged images.") +else: + print("RESULT: Merge may have issues.") +print("=" * 50)