為何出現此錯誤訊息?Use the existing version catalog reference (libs.google.play.app.update) instead

 

📌 錯誤原因

你的 build.gradle.kts 檔案(Kotlin DSL)可能使用了 版本目錄(Version Catalogs),所以無法直接使用:

kotlin
implementation("com.google.android.play:app-update:2.1.0") implementation("com.google.android.play:app-update-ktx:2.1.0")

🔹 Gradle 提示你應該使用 libs.google.play.app.update 這類的 版本目錄引用方式


🛠 解決方案

🔹 方法 1:改用 libs 版本目錄

如果你的專案 已啟用 libs.versions.toml 來管理依賴,請改成:

kotlin
implementation(libs.google.play.app.update) implementation(libs.google.play.app.update.ktx)

✅ 這樣可以確保依賴是從 gradle/libs.versions.toml 管理的。


🔹 方法 2:手動新增 libs.versions.toml 設定

如果你沒有 libs.versions.toml,但希望使用版本目錄,可以這樣做:

  1. 打開 gradle/libs.versions.toml(如果沒有,請自行建立)。

  2. 新增 Play Core Library 的依賴

    toml
    [versions] google-play = "2.1.0" [libraries] google-play-app-update = { module = "com.google.android.play:app-update", version.ref = "google-play" } google-play-app-update-ktx = { module = "com.google.android.play:app-update-ktx", version.ref = "google-play" }
  3. 然後在 build.gradle.kts 使用

    kotlin
    implementation(libs.google.play.app.update) implementation(libs.google.play.app.update.ktx)

🔹 方法 3:關閉版本目錄,直接使用原本的 implementation

如果你不想使用 版本目錄,你可以手動關閉這個功能,在 settings.gradle.kts 移除:

kotlin
enableFeaturePreview("VERSION_CATALOGS")

然後就可以直接使用:

kotlin
implementation("com.google.android.play:app-update:2.1.0") implementation("com.google.android.play:app-update-ktx:2.1.0")

注意:這樣做可能不符合 Gradle 的推薦設定,長遠來看不建議這樣處理。


🔹 結論

最佳做法:如果你的專案已經啟用了 libs.versions.toml,應該使用 方法 1 或 2
⚠ 如果你想繼續使用傳統 implementation("xxx"),則可以 使用方法 3 來關閉版本目錄功能。

為何出現此錯誤訊息?Call requires API level 23 (current min is 21): android.app.AlarmManager#setAndAllowWhileIdle

 你的 minSdkVersion 設定為 21 (Android 5.0 Lollipop),但 setAndAllowWhileIdle() 需要 API 23 (Android 6.0 Marshmallow),因此編譯時會報錯。


解決方法

1. 加入 API 檢查 (Build.VERSION.SDK_INT)

你可以加上條件判斷,讓 API 23 以上 使用 setAndAllowWhileIdle(),而 API 21-22 則使用 setExact()

kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23+ alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) } else { // API 21-22 alarmManager.setExact( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) }

🔹 這樣可以確保 低於 API 23 的裝置仍然可以執行鬧鐘


2. (可選) 提高 minSdkVersion 至 23

如果你的應用 不需要支援 Android 5 (Lollipop),可以直接在 build.gradle 修改 minSdkVersion

gradle
android { defaultConfig { minSdkVersion 23 // 提高最低版本至 Android 6.0 } }

🔹 這樣就可以直接使用 setAndAllowWhileIdle(),不需要額外的 API 檢查。


最佳解法

如果你 希望支援 API 21 (Android 5)又要在 API 23 以上降低耗電,建議這樣寫:

kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12+ 需要特別權限 val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if (!alarmManager.canScheduleExactAlarms()) { val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM) startActivity(intent) } } val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23+ alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) } else { // API 21-22 alarmManager.setExact( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) }

🔹 這樣可以:

  • API 21-22:使用 setExact(),保證準時執行

  • API 23+:使用 setAndAllowWhileIdle(),允許低耗電模式

  • API 31+ (Android 12+):自動請求 SCHEDULE_EXACT_ALARM 權限

這樣你的鬧鐘就能在 各個 Android 版本 上正常運作 🎯

為何出現此錯誤訊息?Setting Exact alarms with setExact requires the SCHEDULE_EXACT_ALARM permission or power exemption from user; it is intended for applications where the user knowingly schedules actions to happen at a precise time such as alarms, clocks, calendars, etc. Check out the javadoc on this permission to make sure your use case is valid.

 你的 setExact() 需要 SCHEDULE_EXACT_ALARM 權限,因為 Android 12 (API 31) 之後,精確鬧鐘 (Exact Alarm) 需要特別授權。你可以用以下方式解決這個錯誤。


解決方案

1. 在 AndroidManifest.xml 內加入 SCHEDULE_EXACT_ALARM 權限

AndroidManifest.xml 檔案內加入:

xml
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>

🔹 這允許應用使用 精確鬧鐘,但 Android 12+ 仍需要使用者手動授權


2. 確保使用者已授權 SCHEDULE_EXACT_ALARM

MainActivityApplication 啟動時檢查:

kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12+ val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if (!alarmManager.canScheduleExactAlarms()) { val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM) startActivity(intent) } }

🔹 這段程式碼會引導使用者進入 系統設定頁面,手動允許應用程式使用精確鬧鐘。


3. 使用 setAndAllowWhileIdle() 來替代 (Android 6+ 可用)

如果你的鬧鐘不需要 極度精準的時間,可以改用:

kotlin
alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent )

🔹 這樣就 不需要 SCHEDULE_EXACT_ALARM 權限,但 Android 6+ 進入 Doze 模式時,可能有誤差


結論

方法需要權限進入 Doze 模式後可用適用版本
setExact()SCHEDULE_EXACT_ALARM❌ 可能不執行Android 5+
setAndAllowWhileIdle()❌ 不需要權限✅ 允許低耗電模式Android 6+
setExactAndAllowWhileIdle()SCHEDULE_EXACT_ALARM✅ 允許低耗電模式Android 6+

👉 建議方案

  • 如果你 需要超精準的鬧鐘 → 使用 setExact() + SCHEDULE_EXACT_ALARM 並提示使用者授權

  • 如果 允許些微時間誤差 → 直接使用 setAndAllowWhileIdle()不需要額外權限 🎯

為何出現此錯誤訊息?No value passed for parameter 'appUpdateType'

 這個錯誤是因為在使用 AppUpdateOptions 時,未正確傳遞 appUpdateType 參數。從你提供的錯誤信息來看,你在創建 AppUpdateOptions 時需要傳遞一個 appUpdateType

在最新的 AppUpdateManager API 中,更新流程的構建需要明確指定更新類型。你應該在構建 AppUpdateOptions 時傳遞 appUpdateType 參數。

修正方案:

你應該像這樣設置 appUpdateType,使用 AppUpdateType.IMMEDIATE 或者 AppUpdateType.FLEXIBLE 來指定更新的類型。

修改後的代碼:

kotlin
private fun handleAppUpdate(appUpdateInfo: AppUpdateInfo) { val currentVersionCode = getCurrentAppVersionCode() // 檢查更新狀態,並比較版本碼 if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && appUpdateInfo.availableVersionCode() > currentVersionCode ) { try { // 創建 AppUpdateOptions,並設置更新類型 val appUpdateOptions = AppUpdateOptions.newBuilder() .setAppUpdateType(AppUpdateType.IMMEDIATE) // 設定即時更新 .build() // 使用 AppUpdateOptions 配置來進行即時更新 appUpdateManager.startUpdateFlow( appUpdateInfo, // 傳遞更新資訊 this, // 傳遞 Activity appUpdateOptions // 傳遞 AppUpdateOptions ) } catch (e: Exception) { // 處理錯誤 Toast.makeText(this, "啟動更新時發生錯誤: ${e.message}", Toast.LENGTH_LONG).show() } } }

主要更新:

  • appUpdateOptions 現在明確設置了 appUpdateType 參數,這樣可以正確傳遞更新類型。

注意:

  • AppUpdateType.IMMEDIATE 是即時更新,表示應用會強制更新並中斷當前操作。如果你希望使用彈性更新(即不強制要求用戶立刻更新),可以使用 AppUpdateType.FLEXIBLE

為何出現此錯誤訊息?Type mismatch: inferred type is Int but Activity was expected。Type mismatch: inferred type is MainActivity but AppUpdateOptions was expected

 這兩個錯誤是由於 startUpdateFlow() 方法的參數類型不匹配引起的。下面我會針對這兩個錯誤進行修正。

錯誤 1: Type mismatch: inferred type is Int but Activity was expected

  • 這個錯誤是由於傳遞的參數 AppUpdateType.IMMEDIATE 應該是更新類型,而 startUpdateFlow() 需要傳遞一個 Activity 作為參數。通常這個錯誤會出現在 startUpdateFlow() 的方法調用中,你可能錯誤地傳遞了錯誤的類型。

錯誤 2: Type mismatch: inferred type is MainActivity but AppUpdateOptions was expected

  • 第二個錯誤是由於 AppUpdateManager.startUpdateFlow() 方法需要使用 AppUpdateOptions,而不是傳遞 Activity 作為參數。這是因為新版的 API 使用 AppUpdateOptions 來處理更新的配置。

修改方案:

  1. 錯誤 1:確保你正確傳遞了 Activity 類型,而不是錯誤的參數。

  2. 錯誤 2:需要創建 AppUpdateOptions 來配置更新選項。

修正後的代碼:

kotlin
private fun handleAppUpdate(appUpdateInfo: AppUpdateInfo) { val currentVersionCode = getCurrentAppVersionCode() // 檢查更新狀態,並比較版本碼 if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && appUpdateInfo.availableVersionCode() > currentVersionCode ) { try { // 創建 AppUpdateOptions 來配置更新選項 val appUpdateOptions = AppUpdateOptions.newBuilder() .setAppUpdateType(AppUpdateType.IMMEDIATE) // 設定即時更新 .build() // 使用 AppUpdateOptions 配置來進行即時更新 appUpdateManager.startUpdateFlow( appUpdateInfo, // 傳遞更新資訊 this, // 傳遞 Activity appUpdateOptions // 配置 AppUpdateOptions ) } catch (e: Exception) { // 處理錯誤 Toast.makeText(this, "啟動更新時發生錯誤: ${e.message}", Toast.LENGTH_LONG).show() } } }

主要更改:

  1. 創建 AppUpdateOptions

    • 我們使用 AppUpdateOptions.newBuilder() 來構建更新配置,並設置更新類型(這裡使用 IMMEDIATE 即時更新)。

  2. 傳遞 AppUpdateOptions 參數

    • startUpdateFlow() 方法中,我們需要傳遞 AppUpdateOptions 作為一個參數,這樣可以正確啟動更新流程。

測試更新:

  • 嘗試這樣的修改後,再次測試你的應用,看看是否能夠正確啟動更新流程。如果還有其他錯誤或疑問,請告訴我,我將幫助你進一步調整!

為何出現此錯誤訊息?'startUpdateFlowForResult(AppUpdateInfo, Int, Activity, Int): Boolean' is deprecated. Deprecated in Java

 這個錯誤是由於 startUpdateFlowForResult() 方法已經被標註為過時(deprecated),Google Play 應用更新 API 最近有些更新。你需要使用新的更新流程,這個流程不再直接依賴 startUpdateFlowForResult(),而是應該使用 AppUpdateManager 的新方法來進行更新。

新的方法是 startUpdateFlow(),它不再返回 boolean 值,而是直接啟動更新流程。

這是更新後的代碼:

kotlin
private fun handleAppUpdate(appUpdateInfo: AppUpdateInfo) { val currentVersionCode = getCurrentAppVersionCode() // 檢查更新狀態,並比較版本碼 if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) && appUpdateInfo.availableVersionCode() > currentVersionCode ) { try { // 使用新方法進行即時更新 appUpdateManager.startUpdateFlow( appUpdateInfo, // 傳遞更新資訊 AppUpdateType.IMMEDIATE, // 設定即時更新 this // 傳遞 Activity ) } catch (e: Exception) { // 處理錯誤 Toast.makeText(this, "啟動更新時發生錯誤: ${e.message}", Toast.LENGTH_LONG).show() } } }

主要改動:

  • 我們將 startUpdateFlowForResult() 方法替換為新的 startUpdateFlow() 方法。

  • startUpdateFlow() 直接啟動更新流程,不再需要傳遞請求代碼或返回結果,這樣可以簡化流程。

需要注意的:

  • Google Play 更新流程已經改變,可能會有些微不同的處理邏輯,特別是在結果回調方面。如果需要處理更新結果,可以通過 OnActivityResult()AppUpdateManager 提供的回調進行處理。

這樣修改之後,應該能避免使用過時的 API 並按照新的方式進行應用更新。

為何出現此錯誤訊息?Variable 'dispatcher' is never used

 這個錯誤是因為你將 dispatcher 變數初始化了,但是沒有實際使用它,導致編譯器給出了這個警告。

如果你希望使用新的 OnBackPressedDispatcher 方法來處理返回鍵事件,並且不需要 dispatcher 變數,你可以直接使用 onBackPressedDispatcher.onBackPressed()。這樣做會讓代碼變得更簡潔。

這裡是更新的代碼:

修正後的代碼:

kotlin
override fun onBackPressed() { if (isExit) { super.onBackPressed() // 這會觸發默認的返回行為 finishAffinity() Handler(Looper.getMainLooper()).postDelayed({ android.os.Process.killProcess(android.os.Process.myPid()) exitProcess(0) }, 100) // 延遲 100 毫秒,確保 Activity 有機會關閉 } else { // 顯示提示在畫面正中央 val toast = Toast.makeText(this, "再按一次返回鍵離開APP", Toast.LENGTH_SHORT) toast.setGravity(android.view.Gravity.CENTER, 0, 600) // 設置為畫面正中央 toast.show() isExit = true // 標記為已經顯示過提示 // 設置延時重置退出標記 // 延時 2 秒後重置 isExit 標記,防止用戶按一次後馬上點擊其他操作 handler.postDelayed({ isExit = false }, 2000) // 2 秒內若沒有再次按下返回鍵則重置標記 } }

這樣就不再需要 dispatcher 變數了,並且可以避免出現 Variable 'dispatcher' is never used 的錯誤。

解釋:

  • onBackPressedDispatcher.onBackPressed() 用於觸發系統的返回操作,這是新版 API 中處理返回鍵的方法。

  • 上述代碼中依然保留了退出應用的邏輯,只是用新的 API 來處理返回鍵的行為。

希望這樣能解決你的問題!

為何出現此錯誤訊息?Suspicious indentation: This is indented but is not continuing the previous expression (backgroundStartTime ...) (Previous statement here)

 你遇到的錯誤是由於不正確的縮排(indentation)引起的。當然,這樣的錯誤可能會讓程式解析器無法識別你的代碼邏輯。

在 Kotlin 中,縮排非常重要,正確的縮排能確保代碼的邏輯清晰明了。根據你的錯誤訊息,似乎是 backgroundStartTime = System.currentTimeMillis() 這一行縮排不正確。這行代碼應該與 super.onDestroy() 同級,或者應該放在適當的程式邏輯塊內。

修正方案:

請確保所有代碼行的縮排一致,且正確結構。根據你的邏輯,下面是正確的寫法:

kotlin
override fun onDestroy() { super.onDestroy() // 正確的縮排 backgroundStartTime = System.currentTimeMillis() // 正常退出應用程式 android.os.Process.killProcess(android.os.Process.myPid()) exitProcess(0) }

縮排解釋:

  • super.onDestroy() 是首先執行的,縮排為 0 層(最外層)。

  • backgroundStartTime = System.currentTimeMillis() 需要位於 onDestroy() 內,所以它的縮排應該是 1 層,與 super.onDestroy() 在同一層級。

  • android.os.Process.killProcess()exitProcess(0) 也是在 onDestroy() 方法內部,所以它們的縮排應該是與 backgroundStartTime 一致。

這樣就能避免不正確的縮排導致的錯誤。

為何出現此錯誤訊息?'onBackPressed(): Unit' is deprecated. This method has been deprecated in favor of using the
{@link OnBackPressedDispatcher} via {@link #getOnBackPressedDispatcher()}.
The OnBackPressedDispatcher controls how back button events are dispatched
to one or more {@link OnBackPressedCallback} objects.

 你的 onBackPressed() 方法已經被 deprecated(過時),Google 建議使用 OnBackPressedDispatcher 來處理返回操作。


✅ 解決方案

1️⃣ 改用 onBackPressedDispatcher

onBackPressedDispatcher 提供更靈活的方式來處理返回事件:

kotlin
findViewById<Button>(R.id.webviewbutton21).setOnClickListener { vibratePhone() // 振動手機(如果需要) if (webView.canGoBack()) { webView.goBack() // WebView 返回上一頁 } else { onBackPressedDispatcher.onBackPressed() // 使用新方法 } }

為什麼要改?

🚨 onBackPressed() 為什麼過時?

  • 舊版 onBackPressed() 會直接關閉 Activity,但新版本 Android 提供 更多控制,可以監聽返回行為、攔截事件,甚至自定義動作。

  • 使用 OnBackPressedDispatcher 可以更靈活處理多個 onBackPressed 事件,例如:

    • WebView 返回(如果可用)

    • 彈出對話框(如果有)

    • 關閉 Activity(如果沒有其他可返回的內容)


2️⃣ 如果你的 Activity 需要監聽返回鍵

如果你的 Activity 需要在全域監聽返回事件,例如 WebView 內返回:

kotlin
private val onBackPressedCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { if (webView.canGoBack()) { webView.goBack() } else { finish() // 直接結束 Activity } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 綁定 OnBackPressedDispatcher onBackPressedDispatcher.addCallback(this, onBackPressedCallback) }

這樣,即使用戶按下手機返回鍵,也能先檢查 WebView 是否能回上一頁,否則才會關閉 Activity


結論

  • 單次返回事件onBackPressedDispatcher.onBackPressed()

  • 全域返回監聽OnBackPressedCallback + onBackPressedDispatcher

  • 不要再用 onBackPressed(),它已經被淘汰!

這樣你的返回行為會更流暢,適用於新版本 Android 🚀

為何出現此錯誤訊息?This declaration overrides deprecated member but not marked as deprecated itself. Please add @Deprecated annotation or suppress. See https://youtrack.jetbrains.com/issue/KT-47902 for details

 你的程式碼出現這個錯誤,是因為 shouldOverrideUrlLoading(view: WebView, url: String): Boolean 已經被標記為過時(deprecated)


解決方案

正確寫法(使用新方法)

Google 在 API 24(Android 7.0)後,推薦使用

kotlin
override fun shouldOverrideUrlLoading( view: WebView, request: WebResourceRequest ): Boolean { val url = request.url.toString() return if (isGoogleMapsUrl(url)) { try { openGoogleMapsApp(url) true } catch (e: Exception) { e.printStackTrace() false } } else { false } }

為什麼要改?

❌ 舊方法(已過時)

kotlin
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean

這個方法在 API 24(Android 7.0)後被標記為 @Deprecated,原因是它:

  • 無法正確處理 POST 請求

  • 無法獲取額外 HTTP 標頭

  • 無法準確辨識來源(例如 WebView 還是其他 app 呼叫)

新方法

kotlin
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean

這個新方法:

  • 提供完整的 WebResourceRequest,可讀取完整 URL 與 HTTP 標頭

  • 更安全且符合新的 WebView 行為


如果你的 App 支援舊版 Android(API < 24)

如果你的 app 需要支援舊版 Android,則可以同時實作新舊方法

kotlin
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { val url = request.url.toString() return handleUrl(view, url) } // 兼容舊版 Android(API < 24) @Suppress("DEPRECATION") override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { return handleUrl(view, url) } // 抽取共用邏輯 private fun handleUrl(view: WebView, url: String): Boolean { return if (isGoogleMapsUrl(url)) { try { openGoogleMapsApp(url) true } catch (e: Exception) { e.printStackTrace() false } } else { false } }

這樣可以確保:

  • Android 7.0 以上 使用 shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest)

  • Android 6.0 及以下 使用 shouldOverrideUrlLoading(view: WebView, url: String)


結論

  • API 24 以上shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest)

  • 如需兼容舊版 Android,則兩個方法都實作,並用 @Suppress("DEPRECATION") 忽略警告。

這樣你的 WebView 會更穩定、更安全,並且不會再出現警告! 🚀

excel 最大值用max,取最小值但要排除0,要怎麼寫?

 =MIN(IF(A1:A10<>0, A1:A10))