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/license_screen.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(
ProviderScope(
overrides: [
licenseInitialStatusProvider.overrideWithValue(cachedLicenseStatus),
],
child: const MyApp(),
const ProviderScope(
child: MyApp(),
),
);
}

View File

@ -1,52 +1,15 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../services/license_service.dart';
///
///
///
/// main() override
/// licenseStatusProvider loading AsyncData
final licenseInitialStatusProvider = Provider<LicenseStatus?>((ref) => null);
///
///
/// - 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 (_) {
//
}
}
}
/// VPSに問い合わせ
/// [licenseStatusProvider].invalidate()
final licenseStatusProvider = FutureProvider<LicenseStatus>((ref) async {
return LicenseService.checkStatus();
});
/// Pro版かどうか使
///
/// licenseStatusProvider AsyncData
/// false
final isProProvider = Provider<bool>((ref) {
final statusAsync = ref.watch(licenseStatusProvider);
return statusAsync.maybeWhen(

View File

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

View File

@ -515,54 +515,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:
@ -849,10 +801,10 @@ packages:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.6.7"
version: "0.7.2"
json_annotation:
dependency: transitive
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
# 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.
version: 1.0.46+53
version: 1.0.45+52
environment:
sdk: ^3.10.1
@ -46,7 +46,7 @@ dependencies:
device_info_plus: ^10.1.0
http: ^1.2.0
crypto: ^3.0.3 # SHA-256 ハッシュ計算用(キャッシュ)
lucide_icons: 0.257.0
lucide_icons: ^0.257.0
reorderable_grid_view: ^2.2.5
camera: ^0.11.3
path_provider: ^2.1.5
@ -61,7 +61,6 @@ dependencies:
package_info_plus: ^8.1.2
gal: ^2.3.0
shared_preferences: ^2.5.4
flutter_secure_storage: ^9.2.2
# Phase 9: Google Drive Backup
googleapis: ^13.2.0

View File

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

View File

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