From 06b6169ad8f56d616b8df7ef011926ccaeeae62f Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Sat, 10 Oct 2026 03:22:51 +0900 Subject: [PATCH] =?UTF-8?q?Android=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8:?= =?UTF-8?q?=20=EC=84=A4=EC=B9=98=EA=B0=80=20=ED=97=88=EC=9A=A9=EB=8F=BC=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=EB=8D=B0=20"=EC=84=A4=EC=B9=98=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9=20=ED=95=84=EC=9A=94"=20=EC=B0=BD=EC=9D=B4=20?= =?UTF-8?q?=EB=9C=A8=EB=8D=98=20=EA=B2=83=20(170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 폴드 (Android 16 · 허용 allow) 에서 첫 설치 시도에 앱의 "설치 허용 필요" 창이 떴다 (Android 설정 화면은 열리지 않음 - 허용 문제가 아니라 다른 오류). Kotlin 이 installApk 안의 IllegalStateException 을 모두 "허용 필요" 로 보냈기 때문. - Kotlin: 허용이 꺼져 설정 화면을 연 경우만 InstallPermissionNeededException → PERMISSION, 다른 오류는 "예외 이름: 원문" (INSTALL). - 채널 canInstallPackages. Dart: PERMISSION 이 와도 허용돼 있으면 창 없이 다시 연다 (한 번). Co-Authored-By: Claude Opus 5.5 --- .../kotlin/com/jj/jj_mkvmaker/MainActivity.kt | 14 +++++++++--- lib/platform/android/android_updater.dart | 22 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/kotlin/com/jj/jj_mkvmaker/MainActivity.kt b/android/app/src/main/kotlin/com/jj/jj_mkvmaker/MainActivity.kt index bca6eff..4b057d6 100644 --- a/android/app/src/main/kotlin/com/jj/jj_mkvmaker/MainActivity.kt +++ b/android/app/src/main/kotlin/com/jj/jj_mkvmaker/MainActivity.kt @@ -141,12 +141,17 @@ class MainActivity : FlutterActivity() { "installApk" -> try { installApk(call.argument("path") ?: "") result.success(null) - } catch (e: IllegalStateException) { + } catch (e: InstallPermissionNeededException) { // 설치 허용 설정 화면을 열었다: 앱이 받은 파일로 다시 설치하게 따로 알린다 result.error("PERMISSION", e.message ?: e.toString(), null) } catch (e: Exception) { - result.error("INSTALL", e.message ?: e.toString(), null) + // 170: 다른 오류를 "설치 허용 필요" 로 오해하지 않게 (예전에는 IllegalStateException 이면 모두 허용 필요로 보았다) + result.error("INSTALL", "${e.javaClass.simpleName}: ${e.message ?: ""}", null) } + // 170: 지금 이 앱이 다른 앱 (APK) 을 설치할 수 있는지 + "canInstallPackages" -> result.success( + Build.VERSION.SDK_INT < Build.VERSION_CODES.O || packageManager.canRequestPackageInstalls() + ) "openFolder" -> result.success(openFolder(call.argument("path") ?: "")) // 예전 버전으로 되돌리기: Android 는 낮은 버전을 위에 설치하지 못해 앱을 지우는 확인 창을 연다 "uninstallSelf" -> { @@ -205,7 +210,7 @@ class MainActivity : FlutterActivity() { Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:$packageName")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ) - throw IllegalStateException("\"이 출처의 앱 설치 허용\" 을 켠 뒤 다시 [업데이트] 를 누르세요") + throw InstallPermissionNeededException("\"이 출처의 앱 설치 허용\" 을 켠 뒤 다시 [업데이트] 를 누르세요") } // FileProvider 가 내보내는 폴더 (cache/updates) 로 옮긴다 val dir = File(cacheDir, "updates").apply { mkdirs() } @@ -225,6 +230,9 @@ class MainActivity : FlutterActivity() { ) } + /// 170: 설치 허용이 꺼져 있어 설정 화면을 연 경우만 (다른 오류와 구별) + class InstallPermissionNeededException(message: String) : Exception(message) + /// 앱이 켜져 있을 때 다른 앱에서 동영상을 열면 (launchMode singleTop) override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) diff --git a/lib/platform/android/android_updater.dart b/lib/platform/android/android_updater.dart index b3dca60..080bb7c 100644 --- a/lib/platform/android/android_updater.dart +++ b/lib/platform/android/android_updater.dart @@ -104,11 +104,25 @@ class AndroidUpdater implements Updater { /// Android 설치 화면 열기 ([downloaded] 는 APK 파일 경로) @override Future scheduleInstall(String downloaded) async { + for (var attempt = 0;; attempt++) { + try { + await _ch.invokeMethod('installApk', {'path': downloaded}); + return; + } on PlatformException catch (e) { + if (e.code != 'PERMISSION') throw UpdateException(e.message ?? '$e'); + // 170: 허용돼 있는데도 "설치 허용 필요" 창이 뜬 일 (폴드) - 한 번 더 물어 허용돼 있으면 창 없이 다시 연다 + if (attempt == 0 && await canInstallPackages()) continue; + throw InstallPermissionNeeded(e.message ?? '$e'); + } + } + } + + /// 170: 이 앱이 APK 를 설치할 수 있는지 (Android 의 "이 출처의 앱 설치 허용"). 모르면 false + Future canInstallPackages() async { try { - await _ch.invokeMethod('installApk', {'path': downloaded}); - } on PlatformException catch (e) { - if (e.code == 'PERMISSION') throw InstallPermissionNeeded(e.message ?? '$e'); - throw UpdateException(e.message ?? '$e'); + return await _ch.invokeMethod('canInstallPackages') ?? false; + } catch (_) { + return false; } }