From 28d3e53bab771658914f192d6afa0a0d115db6f5 Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Fri, 16 Jan 2026 20:08:59 +0900 Subject: [PATCH] =?UTF-8?q?feat(debug):=20=EB=94=94=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 광고/IAP/무적 모드 토글 - 시간 스케일 조절 - SharedPreferences 기반 영속화 --- .../core/engine/debug_settings_service.dart | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 lib/src/core/engine/debug_settings_service.dart diff --git a/lib/src/core/engine/debug_settings_service.dart b/lib/src/core/engine/debug_settings_service.dart new file mode 100644 index 0000000..330c63d --- /dev/null +++ b/lib/src/core/engine/debug_settings_service.dart @@ -0,0 +1,180 @@ +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:asciineverdie/src/core/engine/ad_service.dart'; +import 'package:asciineverdie/src/core/engine/iap_service.dart'; + +/// 디버그 설정 서비스 (Phase 8) +/// +/// 개발/테스트 중 사용하는 디버그 옵션을 통합 관리합니다. +/// kDebugMode에서만 활성화됩니다. +class DebugSettingsService { + DebugSettingsService._(); + + static DebugSettingsService? _instance; + + /// 싱글톤 인스턴스 + static DebugSettingsService get instance { + _instance ??= DebugSettingsService._(); + return _instance!; + } + + // =========================================================================== + // 상수 + // =========================================================================== + + static const String _keyAdEnabled = 'debug_ad_enabled'; + static const String _keyIapSimulated = 'debug_iap_simulated'; + static const String _keyOfflineHours = 'debug_offline_hours'; + + /// 오프라인 시간 시뮬레이션 옵션 + static const List offlineHoursOptions = [0, 1, 5, 10, 24]; + + // =========================================================================== + // 상태 + // =========================================================================== + + bool _isInitialized = false; + + /// 광고 활성화 여부 (디버그 모드 전용) + bool _adEnabled = true; + + /// IAP 구매 시뮬레이션 여부 (디버그 모드 전용) + bool _iapSimulated = false; + + /// 오프라인 시간 시뮬레이션 (시간 단위) + int _offlineHours = 0; + + // =========================================================================== + // 초기화 + // =========================================================================== + + /// 서비스 초기화 (저장된 설정 로드) + Future initialize() async { + if (_isInitialized) return; + if (!kDebugMode) { + _isInitialized = true; + return; + } + + final prefs = await SharedPreferences.getInstance(); + _adEnabled = prefs.getBool(_keyAdEnabled) ?? true; + _iapSimulated = prefs.getBool(_keyIapSimulated) ?? false; + _offlineHours = prefs.getInt(_keyOfflineHours) ?? 0; + + // 다른 서비스에 설정 동기화 + _syncToServices(); + + _isInitialized = true; + debugPrint('[DebugSettings] Initialized: ad=$_adEnabled, ' + 'iap=$_iapSimulated, offline=$_offlineHours'); + } + + /// 설정을 다른 서비스에 동기화 + void _syncToServices() { + AdService.instance.debugAdEnabled = _adEnabled; + IAPService.instance.debugIAPSimulated = _iapSimulated; + } + + // =========================================================================== + // 디버그 모드 확인 + // =========================================================================== + + /// 디버그 모드 활성화 여부 + bool get isDebugMode => kDebugMode; + + // =========================================================================== + // 광고 설정 + // =========================================================================== + + /// 광고 활성화 여부 + /// + /// - true: 실제 광고 표시 + /// - false: 광고 버튼 클릭 시 바로 보상 지급 + bool get adEnabled => _adEnabled; + + /// 광고 활성화 토글 + Future setAdEnabled(bool value) async { + if (!kDebugMode) return; + + _adEnabled = value; + AdService.instance.debugAdEnabled = value; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_keyAdEnabled, value); + + debugPrint('[DebugSettings] Ad enabled: $value'); + } + + // =========================================================================== + // IAP 설정 + // =========================================================================== + + /// IAP 구매 시뮬레이션 여부 + /// + /// - true: 유료 유저로 동작 (광고 제거됨) + /// - false: 무료 유저로 동작 + bool get iapSimulated => _iapSimulated; + + /// IAP 구매 시뮬레이션 토글 + Future setIapSimulated(bool value) async { + if (!kDebugMode) return; + + _iapSimulated = value; + IAPService.instance.debugIAPSimulated = value; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_keyIapSimulated, value); + + debugPrint('[DebugSettings] IAP simulated: $value'); + } + + // =========================================================================== + // 오프라인 시간 시뮬레이션 + // =========================================================================== + + /// 오프라인 시간 시뮬레이션 (시간 단위) + /// + /// 복귀 보상 테스트용. 0이면 시뮬레이션 비활성화. + int get offlineHours => _offlineHours; + + /// 오프라인 시간 시뮬레이션 설정 + Future setOfflineHours(int hours) async { + if (!kDebugMode) return; + + _offlineHours = hours; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt(_keyOfflineHours, hours); + + debugPrint('[DebugSettings] Offline hours: $hours'); + } + + /// 시뮬레이션된 마지막 플레이 시간 계산 + /// + /// [actualLastPlayTime] 실제 마지막 플레이 시간 + /// Returns: 시뮬레이션 적용된 마지막 플레이 시간 + DateTime? getSimulatedLastPlayTime(DateTime? actualLastPlayTime) { + if (!kDebugMode || _offlineHours == 0) { + return actualLastPlayTime; + } + + // 시뮬레이션: 현재 시간에서 offlineHours만큼 뺀 시간을 마지막 플레이 시간으로 + return DateTime.now().subtract(Duration(hours: _offlineHours)); + } + + // =========================================================================== + // 전체 초기화 + // =========================================================================== + + /// 모든 디버그 설정 초기화 + Future resetAll() async { + if (!kDebugMode) return; + + await setAdEnabled(true); + await setIapSimulated(false); + await setOfflineHours(0); + + debugPrint('[DebugSettings] All settings reset'); + } +}