Compare commits

..

No commits in common. "191274c65a40df1ae4f4fe1f1ae787eae66c3015" and "1bf59e02ccc73d7ccc2980b2f19b1d5714ce59c9" have entirely different histories.

7 changed files with 33 additions and 174 deletions

View File

@ -9,8 +9,6 @@ import 'providers/theme_provider.dart';
import 'screens/main_screen.dart'; import 'screens/main_screen.dart';
import 'screens/license_screen.dart'; import 'screens/license_screen.dart';
import 'services/migration_service.dart'; import 'services/migration_service.dart';
import 'services/license_service.dart';
import 'providers/license_provider.dart';
/// ///
/// ///
@ -76,16 +74,9 @@ void main() async {
} }
} }
// : runApp
// licenseStatusProvider loading AsyncData override
final cachedLicenseStatus = await LicenseService.getCachedStatusOnly();
runApp( runApp(
ProviderScope( const ProviderScope(
overrides: [ child: MyApp(),
licenseInitialStatusProvider.overrideWithValue(cachedLicenseStatus),
],
child: const MyApp(),
), ),
); );
} }

View File

@ -1,52 +1,15 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../services/license_service.dart'; import '../services/license_service.dart';
/// ///
/// ///
/// main() override /// VPSに問い合わせ
/// licenseStatusProvider loading AsyncData /// [licenseStatusProvider].invalidate()
final licenseInitialStatusProvider = Provider<LicenseStatus?>((ref) => null); final licenseStatusProvider = FutureProvider<LicenseStatus>((ref) async {
return LicenseService.checkStatus();
/// });
///
/// - build() licenseInitialStatusProvider
/// -
final licenseStatusProvider =
AsyncNotifierProvider<LicenseStatusNotifier, LicenseStatus>(
LicenseStatusNotifier.new,
);
class LicenseStatusNotifier extends AsyncNotifier<LicenseStatus> {
@override
FutureOr<LicenseStatus> build() {
final initial = ref.read(licenseInitialStatusProvider);
if (initial != null) {
// loading
_refreshFromServer();
return initial;
}
// override :
return LicenseService.checkStatus();
}
///
Future<void> _refreshFromServer() async {
try {
final fresh = await LicenseService.checkStatus();
if (state.hasValue && state.value != fresh) {
state = AsyncData(fresh);
}
} catch (_) {
//
}
}
}
/// Pro版かどうか使 /// Pro版かどうか使
///
/// licenseStatusProvider AsyncData
/// false
final isProProvider = Provider<bool>((ref) { final isProProvider = Provider<bool>((ref) {
final statusAsync = ref.watch(licenseStatusProvider); final statusAsync = ref.watch(licenseStatusProvider);
return statusAsync.maybeWhen( return statusAsync.maybeWhen(

View File

@ -1,6 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'device_service.dart'; import 'device_service.dart';
@ -23,81 +22,43 @@ enum LicenseStatus {
/// ///
/// ///
/// ##
/// - : flutter_secure_storage
/// - : SharedPreferences
///
/// ## /// ##
/// 1. : SecureStorage /// 1. : VPSに問い合わせた結果を使用 + SharedPreferencesにキャッシュ
/// 2. : VPS /// 2. : SharedPreferencesのキャッシュを使用 (Pro状態を維持)
/// 3. : SharedPreferences
/// ///
/// ## /// ##
/// PONSHU-XXXX-XXXX-XXXX (6 = 12hex文字, ) /// PONSHU-XXXX-XXXX-XXXX (6 = 12hex文字, )
class LicenseService { class LicenseService {
static const _secureKeyName = 'ponshu_license_key'; static const _prefLicenseKey = 'ponshu_license_key';
static const _prefCachedStatus = 'ponshu_license_status_cache'; static const _prefCachedStatus = 'ponshu_license_status_cache';
static const _prefCachedAt = 'ponshu_license_cached_at'; static const _prefCachedAt = 'ponshu_license_cached_at';
static const _prefMigratedV1 = 'ponshu_license_migrated_v1'; static const _cacheValidSeconds = 24 * 60 * 60; // 24
static const _cacheValidSeconds = 24 * 60 * 60; // 24
static const _storage = FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
);
// ========== Migration ==========
/// SharedPreferences flutter_secure_storage
static Future<void> _migrateIfNeeded() async {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool(_prefMigratedV1) == true) return;
//
final oldValue = prefs.getString(_secureKeyName);
if (oldValue != null && oldValue.isNotEmpty) {
await _storage.write(key: _secureKeyName, value: oldValue);
await prefs.remove(_secureKeyName);
debugPrint('[License] Migrated license key to secure storage.');
}
await prefs.setBool(_prefMigratedV1, true);
}
// ========== Public API ========== // ========== Public API ==========
/// /// :
///
/// main() await runApp()
static Future<LicenseStatus> getCachedStatusOnly() async {
await _migrateIfNeeded();
final savedKey = await _storage.read(key: _secureKeyName) ?? '';
if (savedKey.isEmpty) return LicenseStatus.free;
final prefs = await SharedPreferences.getInstance();
return _getCachedStatus(prefs);
}
/// : VPS
static Future<LicenseStatus> checkStatus() async { static Future<LicenseStatus> checkStatus() async {
await _migrateIfNeeded(); final prefs = await SharedPreferences.getInstance();
final savedKey = await _storage.read(key: _secureKeyName) ?? ''; final savedKey = prefs.getString(_prefLicenseKey) ?? '';
//
if (savedKey.isNotEmpty) { if (savedKey.isNotEmpty) {
try { try {
final status = await _validateKeyWithServer(savedKey); final status = await _validateKeyWithServer(savedKey);
if (status == LicenseStatus.offline) { if (status == LicenseStatus.offline) {
// :
debugPrint('[License] Server unreachable, using cache'); debugPrint('[License] Server unreachable, using cache');
final prefs = await SharedPreferences.getInstance();
return _getCachedStatus(prefs); return _getCachedStatus(prefs);
} }
final prefs = await SharedPreferences.getInstance();
await _cacheStatus(prefs, status); await _cacheStatus(prefs, status);
return status; return status;
} catch (e) { } catch (e) {
debugPrint('[License] Server unreachable, using cache: $e'); debugPrint('[License] Server unreachable, using cache: $e');
final prefs = await SharedPreferences.getInstance();
return _getCachedStatus(prefs); return _getCachedStatus(prefs);
} }
} }
//
return LicenseStatus.free; return LicenseStatus.free;
} }
@ -115,8 +76,8 @@ class LicenseService {
final status = await _validateKeyWithServer(key); final status = await _validateKeyWithServer(key);
if (status == LicenseStatus.pro) { if (status == LicenseStatus.pro) {
await _storage.write(key: _secureKeyName, value: key);
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefLicenseKey, key);
await _cacheStatus(prefs, LicenseStatus.pro); await _cacheStatus(prefs, LicenseStatus.pro);
debugPrint('[License] Activated successfully.'); debugPrint('[License] Activated successfully.');
return (success: true, message: ''); return (success: true, message: '');
@ -135,14 +96,14 @@ class LicenseService {
/// ///
static Future<bool> hasLicenseKey() async { static Future<bool> hasLicenseKey() async {
await _migrateIfNeeded(); final prefs = await SharedPreferences.getInstance();
return ((await _storage.read(key: _secureKeyName)) ?? '').isNotEmpty; return (prefs.getString(_prefLicenseKey) ?? '').isNotEmpty;
} }
/// ///
static Future<void> reset() async { static Future<void> reset() async {
await _storage.delete(key: _secureKeyName);
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.remove(_prefLicenseKey);
await prefs.remove(_prefCachedStatus); await prefs.remove(_prefCachedStatus);
await prefs.remove(_prefCachedAt); await prefs.remove(_prefCachedAt);
debugPrint('[License] Reset complete.'); debugPrint('[License] Reset complete.');
@ -167,11 +128,7 @@ class LicenseService {
final data = jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>; final data = jsonDecode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
if (data['valid'] == true) return LicenseStatus.pro; if (data['valid'] == true) return LicenseStatus.pro;
// revoked booleanerror if ((data['error'] as String? ?? '').contains('無効化')) return LicenseStatus.revoked;
if (data['revoked'] == true ||
(data['error'] as String? ?? '').contains('無効化')) {
return LicenseStatus.revoked;
}
return LicenseStatus.free; return LicenseStatus.free;
} catch (e) { } catch (e) {
@ -186,18 +143,19 @@ class LicenseService {
} }
static LicenseStatus _getCachedStatus(SharedPreferences prefs) { static LicenseStatus _getCachedStatus(SharedPreferences prefs) {
final cached = prefs.getString(_prefCachedStatus); final cached = prefs.getString(_prefCachedStatus);
final cachedAt = prefs.getString(_prefCachedAt); final cachedAt = prefs.getString(_prefCachedAt);
if (cached == null) return LicenseStatus.free; if (cached == null) return LicenseStatus.free;
// checkStatus // _validateKeyWithServer
// _getCachedStatus // _getCachedStatus
// //
// TTL _cacheValidSeconds = 24h: // TTL _cacheValidSeconds = 24h:
// - free / offline: free // - free / offline free
// - pro : // - pro :
// - revoked: // - revoked:
// TTL isNoExpiryStatus
if (cachedAt != null) { if (cachedAt != null) {
final age = DateTime.now().difference(DateTime.parse(cachedAt)); final age = DateTime.now().difference(DateTime.parse(cachedAt));
final isNoExpiryStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name; final isNoExpiryStatus = cached == LicenseStatus.pro.name || cached == LicenseStatus.revoked.name;

View File

@ -515,54 +515,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
url: "https://pub.dev"
source: hosted
version: "9.2.4"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
url: "https://pub.dev"
source: hosted
version: "1.2.3"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_speed_dial: flutter_speed_dial:
dependency: "direct main" dependency: "direct main"
description: description:
@ -849,10 +801,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: js name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.7" version: "0.7.2"
json_annotation: json_annotation:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.46+53 version: 1.0.45+52
environment: environment:
sdk: ^3.10.1 sdk: ^3.10.1
@ -46,7 +46,7 @@ dependencies:
device_info_plus: ^10.1.0 device_info_plus: ^10.1.0
http: ^1.2.0 http: ^1.2.0
crypto: ^3.0.3 # SHA-256 ハッシュ計算用(キャッシュ) crypto: ^3.0.3 # SHA-256 ハッシュ計算用(キャッシュ)
lucide_icons: 0.257.0 lucide_icons: ^0.257.0
reorderable_grid_view: ^2.2.5 reorderable_grid_view: ^2.2.5
camera: ^0.11.3 camera: ^0.11.3
path_provider: ^2.1.5 path_provider: ^2.1.5
@ -61,7 +61,6 @@ dependencies:
package_info_plus: ^8.1.2 package_info_plus: ^8.1.2
gal: ^2.3.0 gal: ^2.3.0
shared_preferences: ^2.5.4 shared_preferences: ^2.5.4
flutter_secure_storage: ^9.2.2
# Phase 9: Google Drive Backup # Phase 9: Google Drive Backup
googleapis: ^13.2.0 googleapis: ^13.2.0

View File

@ -8,7 +8,6 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h> #include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <gal/gal_plugin_c_api.h> #include <gal/gal_plugin_c_api.h>
#include <printing/printing_plugin.h> #include <printing/printing_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
@ -19,8 +18,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
GalPluginCApiRegisterWithRegistrar( GalPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GalPluginCApi")); registry->GetRegistrarForPlugin("GalPluginCApi"));
PrintingPluginRegisterWithRegistrar( PrintingPluginRegisterWithRegistrar(

View File

@ -5,7 +5,6 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus connectivity_plus
file_selector_windows file_selector_windows
flutter_secure_storage_windows
gal gal
printing printing
share_plus share_plus