diff --git a/app/src/main/java/com/ahu/ahutong/MainActivity.kt b/app/src/main/java/com/ahu/ahutong/MainActivity.kt index 242c582..6ae1977 100644 --- a/app/src/main/java/com/ahu/ahutong/MainActivity.kt +++ b/app/src/main/java/com/ahu/ahutong/MainActivity.kt @@ -46,6 +46,9 @@ import com.ahu.ahutong.personalization.runtime.BehaviorPredictionRuntime import com.ahu.ahutong.personalization.action.ActionSource import java.io.File import java.security.MessageDigest +import kotlinx.coroutines.delay + +private const val DEBUG_BUILD_NOTICE_DURATION_MS = 3_000L @AndroidEntryPoint class MainActivity : ComponentActivity() { @@ -147,23 +150,27 @@ class MainActivity : ComponentActivity() { behaviorRuntime = behaviorRuntime, diagnosticsContribution = diagnosticsContribution, paymentQrCommands = paymentQrCommands, - isReLoginShown = isReLoginDialogShown, - onReLoginDismiss = { isReLoginDialogShown = false } - ) - } - } - } - + isReLoginShown = isReLoginDialogShown, + onReLoginDismiss = { isReLoginDialogShown = false } + ) + } + } + + showDebugBuildNotice(savedInstanceState) + } + private fun init() { lifecycleScope.launchSafe { if (AHUCache.isPrivacyAccepted()) { AHUCache.getCurrentUser()?.xh?.takeIf { it.isNotBlank() }?.let { behaviorRuntime.startProfile(it) } } } - lifecycleScope.launchSafe { - mainViewModel.checkApkUpdate(this@MainActivity) - } - WidgetUpdateScheduler.scheduleNext(this@MainActivity) + if (!BuildConfig.DEBUG) { + lifecycleScope.launchSafe { + mainViewModel.checkApkUpdate(this@MainActivity) + } + } + WidgetUpdateScheduler.scheduleNext(this@MainActivity) RustSDK.loadLibrary(context = applicationContext) @@ -203,7 +210,23 @@ class MainActivity : ComponentActivity() { setIntent(intent) if (intent.data != null) behaviorRuntime.markNextNavigationSource(ActionSource.DEEPLINK) } -// private fun checkApkUpdateOnStartup() { + + private fun showDebugBuildNotice(savedInstanceState: Bundle?) { + if (!BuildConfig.DEBUG || savedInstanceState != null) return + + val toast = Toast.makeText( + this, + R.string.debug_build_notice, + Toast.LENGTH_LONG + ) + toast.show() + lifecycleScope.launchSafe { + delay(DEBUG_BUILD_NOTICE_DURATION_MS) + toast.cancel() + } + } + +// private fun checkApkUpdateOnStartup() { // Log.i("ApkUpdate", "start checkApkUpdateOnStartup") // // lifecycleScope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/ahu/ahutong/sdk/RustSDK.kt b/app/src/main/java/com/ahu/ahutong/sdk/RustSDK.kt index 3908516..76a96b9 100644 --- a/app/src/main/java/com/ahu/ahutong/sdk/RustSDK.kt +++ b/app/src/main/java/com/ahu/ahutong/sdk/RustSDK.kt @@ -2,6 +2,7 @@ package com.ahu.ahutong.sdk import android.content.Context import android.util.Log +import com.ahu.ahutong.BuildConfig import com.ahu.ahutong.data.model.Card import com.ahu.ahutong.data.model.Course import com.ahu.ahutong.data.model.User @@ -89,6 +90,14 @@ object RustSDK { Log.d(TAG_HOTUPDATE, "Checking custom lib at: ${customLib.absolutePath}, exists: ${customLib.exists()}") + if (BuildConfig.DEBUG && customLib.exists()) { + if (customLib.delete()) { + Log.i(TAG_HOTUPDATE, "removed cached hot-update library for debug build") + } else { + Log.w(TAG_HOTUPDATE, "failed to remove cached hot-update library for debug build") + } + } + if (currentVersionCode > lastVersionCode) { Log.i(TAG_HOTUPDATE, "App updated ($lastVersionCode -> $currentVersionCode), clearing hotUpdate libs") if (customLib.exists()) { @@ -97,7 +106,7 @@ object RustSDK { prefs.edit {putLong("app_version_code", currentVersionCode)} } - if (customLib.exists()) { + if (!BuildConfig.DEBUG && customLib.exists()) { try { // 尝试预加载 C++ 运行时,防止热更新 SO 找不到依赖 try { System.loadLibrary("c++_shared") } catch (e: Throwable) { Log.w(TAG_HOTUPDATE, "Failed to load c++_shared: ${e.message}") } @@ -139,6 +148,12 @@ object RustSDK { onSuccess: (() -> Unit)? = null, onFinished: (() -> Unit)? = null ) { + if (BuildConfig.DEBUG) { + Log.i(TAG_HOTUPDATE, "native hot update check disabled for debug build") + onFinished?.invoke() + return + } + if (!android.os.Process.is64Bit()) { Log.w(TAG_HOTUPDATE, "current device is not a 64-bit environment, skip the hotUpdate") onFinished?.invoke() diff --git a/app/src/main/java/com/ahu/ahutong/ui/screen/main/Home.kt b/app/src/main/java/com/ahu/ahutong/ui/screen/main/Home.kt index 4da233a..493b540 100644 --- a/app/src/main/java/com/ahu/ahutong/ui/screen/main/Home.kt +++ b/app/src/main/java/com/ahu/ahutong/ui/screen/main/Home.kt @@ -11,11 +11,20 @@ import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.BugReport +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -28,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.Alignment import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.input.pointer.PointerEventPass @@ -35,9 +45,13 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.ahu.ahutong.BuildConfig +import com.ahu.ahutong.R import com.ahu.ahutong.data.dao.AHUCache import com.ahu.ahutong.data.schedule.CurrentWeekResolver import androidx.navigation.NavHostController @@ -300,6 +314,9 @@ fun Home( navController = navController, enabled = !isEditingHome, trailingContent = { + if (BuildConfig.DEBUG) { + DebugBuildBadge() + } if ( !isEditingHome && weatherHomeConfig.showOnHome && @@ -420,6 +437,35 @@ fun Home( } } +@Composable +private fun DebugBuildBadge() { + Surface( + color = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError, + shape = androidx.compose.foundation.shape.RoundedCornerShape(6.dp), + shadowElevation = 4.dp + ) { + Row( + modifier = Modifier + .height(28.dp) + .padding(horizontal = 8.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Rounded.BugReport, + contentDescription = null, + modifier = Modifier.size(16.dp) + ) + Text( + text = stringResource(R.string.debug_build_badge), + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.Bold + ) + } + } +} + private fun normalizeHomeWidgetSlots(slots: List): List { val knownIds = HomeWidgetRegistry.widgetById.keys val seen = mutableSetOf() diff --git a/app/src/main/java/com/ahu/ahutong/ui/state/MainViewModel.kt b/app/src/main/java/com/ahu/ahutong/ui/state/MainViewModel.kt index cab5a9c..54de81b 100644 --- a/app/src/main/java/com/ahu/ahutong/ui/state/MainViewModel.kt +++ b/app/src/main/java/com/ahu/ahutong/ui/state/MainViewModel.kt @@ -156,6 +156,11 @@ class MainViewModel : ViewModel() { * 全部在 IO 线程执行,不阻塞主线程 */ suspend fun checkApkUpdate(context: Context) = withContext(Dispatchers.IO) { + if (BuildConfig.DEBUG) { + Log.i("ApkUpdate", "startup update check disabled for debug build") + return@withContext + } + val dir = context.getExternalFilesDir(null) ?: context.filesDir // 1. 清理版本号 <= 当前版本的残留 APK(安全校验文件名) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1acc01c..2c9ed02 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,6 +67,8 @@ 教务系统 搜索 检查更新 + DEBUG + 正在运行调试版本,自动更新已被禁用。 更新日志 导入课表 手动添加课程