diff --git a/lib/bridge/web_view_bridge_handler.dart b/lib/bridge/web_view_bridge_handler.dart index bde01a9..487aef8 100644 --- a/lib/bridge/web_view_bridge_handler.dart +++ b/lib/bridge/web_view_bridge_handler.dart @@ -11,6 +11,7 @@ import 'package:logger/logger.dart'; import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:app/utils/url_restore_manager.dart'; class WebViewBridgeHandler { final BuildContext context; @@ -219,6 +220,13 @@ class WebViewBridgeHandler { } try { + // Android에서 카메라 호출 전 현재 URL 저장 (앱이 kill될 경우 복원용) + final currentUrl = await controller.getUrl(); + if (currentUrl != null) { + await UrlRestoreManager.saveUrlBeforeCamera(currentUrl.toString()); + logger.d('카메라 호출 전 URL 저장: $currentUrl'); + } + onShowLoading?.call('사진 촬영 중...'); final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage( @@ -235,9 +243,14 @@ class WebViewBridgeHandler { source: "openAlbum('data:image/png;base64,$base64Image')", ); } + + // 정상 복귀 시 저장된 URL 삭제 (앱이 kill되지 않았으므로) + await UrlRestoreManager.clearSavedUrl(); onHideLoading?.call(); } catch (e) { logger.e('Error taking image: $e'); + // 에러 시에도 저장된 URL 삭제 + await UrlRestoreManager.clearSavedUrl(); onHideLoading?.call(); } } diff --git a/lib/main.dart b/lib/main.dart index 3969bcd..7237042 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:app/permissions/FirebaseConfig.dart'; import 'package:app/ui/loading_widget.dart'; import 'package:app/utils/env/env.dart'; +import 'package:app/utils/url_restore_manager.dart'; import 'package:app/web_view/web_view.dart'; import 'package:flutter/material.dart'; import 'package:kakao_flutter_sdk/kakao_flutter_sdk_template.dart'; @@ -142,25 +143,44 @@ class SplashScreen extends StatefulWidget { } class _SplashScreenState extends State { + String? _restoredUrl; + @override void initState() { super.initState(); + _initializeAndNavigate(); + } - Future.delayed(const Duration(seconds: 3), () { - Navigator.of(context).pushReplacement( - PageRouteBuilder( - pageBuilder: (context, animation, secondaryAnimation) => - const BottleNoteWebView(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition( - opacity: animation, - child: child, - ); - }, - transitionDuration: const Duration(milliseconds: 1000), - ), - ); - }); + Future _initializeAndNavigate() async { + try { + // Android에서 카메라 호출 후 앱이 kill된 경우 복원할 URL 확인 + final restoredUrl = await UrlRestoreManager.consumeRestoredUrl(); + if (restoredUrl != null) { + logger.d('복원할 URL 발견: $restoredUrl'); + _restoredUrl = restoredUrl; + } + + // 최소 3초 대기 (기존 동작 유지) + await Future.delayed(const Duration(seconds: 3)); + } catch (e) { + logger.e('스플래시 초기화 중 오류 발생: $e'); + } + + if (!mounted) return; + + Navigator.of(context).pushReplacement( + PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => + BottleNoteWebView(initialUrl: _restoredUrl), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return FadeTransition( + opacity: animation, + child: child, + ); + }, + transitionDuration: const Duration(milliseconds: 1000), + ), + ); } @override diff --git a/lib/utils/url_restore_manager.dart b/lib/utils/url_restore_manager.dart new file mode 100644 index 0000000..ee45956 --- /dev/null +++ b/lib/utils/url_restore_manager.dart @@ -0,0 +1,57 @@ +import 'dart:io' show Platform; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Android에서 카메라 호출 후 앱이 kill되었을 때 URL을 복원하기 위한 매니저 +class UrlRestoreManager { + static const String _pendingUrlKey = 'pending_restore_url'; + static const String _timestampKey = 'pending_restore_timestamp'; + + /// URL 복원 유효 시간 (5분) + static const int _validDurationMinutes = 5; + + /// 카메라 호출 전 현재 URL 저장 (Android 전용) + static Future saveUrlBeforeCamera(String url) async { + if (!Platform.isAndroid) return; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_pendingUrlKey, url); + await prefs.setInt(_timestampKey, DateTime.now().millisecondsSinceEpoch); + } + + /// 저장된 URL 가져오기 및 삭제 (Android 전용) + /// 유효 시간이 지났거나 URL이 없으면 null 반환 + static Future consumeRestoredUrl() async { + if (!Platform.isAndroid) return null; + + final prefs = await SharedPreferences.getInstance(); + final savedUrl = prefs.getString(_pendingUrlKey); + final savedTimestamp = prefs.getInt(_timestampKey); + + // 저장된 URL이 없으면 null + if (savedUrl == null || savedTimestamp == null) { + return null; + } + + // 유효 시간 체크 (5분 초과시 무효) + final now = DateTime.now().millisecondsSinceEpoch; + final elapsed = now - savedTimestamp; + if (elapsed > _validDurationMinutes * 60 * 1000) { + // 만료된 데이터도 정리 + await clearSavedUrl(); + return null; + } + + // 정상적으로 복원된 경우 사용 후 삭제 + await clearSavedUrl(); + return savedUrl; + } + + /// 저장된 URL 삭제 (정상 카메라 복귀 시 호출) + static Future clearSavedUrl() async { + if (!Platform.isAndroid) return; + + final prefs = await SharedPreferences.getInstance(); + await prefs.remove(_pendingUrlKey); + await prefs.remove(_timestampKey); + } +} diff --git a/lib/web_view/web_view.dart b/lib/web_view/web_view.dart index 9fa4950..03970d2 100644 --- a/lib/web_view/web_view.dart +++ b/lib/web_view/web_view.dart @@ -17,7 +17,9 @@ import '../actions/back_action_handler.dart'; class BottleNoteWebView extends StatefulWidget { final VoidCallback? onLoaded; - const BottleNoteWebView({super.key, this.onLoaded}); + final String? initialUrl; + + const BottleNoteWebView({super.key, this.onLoaded, this.initialUrl}); @override State createState() => BottleNoteWebViewState(); @@ -113,7 +115,9 @@ class BottleNoteWebViewState extends State final content = Stack( children: [ InAppWebView( - initialUrlRequest: URLRequest(url: WebUri(Env.webViewUrl)), + initialUrlRequest: URLRequest( + url: WebUri(widget.initialUrl ?? Env.webViewUrl), + ), initialSettings: InAppWebViewSettings( javaScriptEnabled: true, useShouldOverrideUrlLoading: true, diff --git a/pubspec.lock b/pubspec.lock index 96932e8..0bae6c9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1017,7 +1017,7 @@ packages: source: hosted version: "4.1.0" shared_preferences: - dependency: transitive + dependency: "direct main" description: name: shared_preferences sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a" diff --git a/pubspec.yaml b/pubspec.yaml index 9750997..c8b65b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: flutter_svg: ^2.0.10+1 svg_path_parser: ^1.1.2 flutter_native_splash: ^2.4.6 + shared_preferences: ^2.5.2 dev_dependencies: flutter_test: