2026-01-15 15:53:44 +00:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
2026-04-10 23:14:37 +00:00
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
|
import 'package:uuid/uuid.dart';
|
2026-01-15 15:53:44 +00:00
|
|
|
|
|
|
|
|
|
|
/// デバイスID取得サービス
|
2026-04-10 23:14:37 +00:00
|
|
|
|
/// レート制限・ライセンス管理のためのデバイス識別に使用
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## 安定性の保証
|
|
|
|
|
|
/// - Android: ANDROID_ID をハッシュ化して使用(Factory Reset まで不変)
|
|
|
|
|
|
/// - iOS: SharedPreferences に UUID を永続化(identifierForVendor は
|
|
|
|
|
|
/// 全ベンダーアプリ削除後の再インストールで変わるため不使用)
|
|
|
|
|
|
/// - その他/エラー時: SharedPreferences に UUID を永続化
|
2026-01-15 15:53:44 +00:00
|
|
|
|
class DeviceService {
|
|
|
|
|
|
static final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
|
|
|
|
|
static String? _cachedDeviceId;
|
|
|
|
|
|
|
2026-04-10 23:14:37 +00:00
|
|
|
|
static const _prefKey = 'ponshu_device_id';
|
|
|
|
|
|
|
2026-01-15 15:53:44 +00:00
|
|
|
|
/// デバイス固有のIDを取得(SHA256ハッシュ化)
|
|
|
|
|
|
static Future<String> getDeviceId() async {
|
2026-04-10 23:14:37 +00:00
|
|
|
|
if (_cachedDeviceId != null) return _cachedDeviceId!;
|
|
|
|
|
|
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
|
|
|
|
|
|
// SharedPreferencesに永続化済みならそれを使う(再インストール後も同じIDを返す)
|
|
|
|
|
|
final stored = prefs.getString(_prefKey);
|
|
|
|
|
|
if (stored != null && stored.isNotEmpty) {
|
|
|
|
|
|
_cachedDeviceId = stored;
|
|
|
|
|
|
debugPrint('[Device] Using persisted device ID: ${stored.substring(0, 8)}...');
|
2026-01-15 15:53:44 +00:00
|
|
|
|
return _cachedDeviceId!;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 23:14:37 +00:00
|
|
|
|
// 初回: ハードウェアIDから生成してSharedPreferencesに保存
|
|
|
|
|
|
final id = await _generateAndPersist(prefs);
|
|
|
|
|
|
_cachedDeviceId = id;
|
|
|
|
|
|
return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Future<String> _generateAndPersist(SharedPreferences prefs) async {
|
|
|
|
|
|
String deviceIdentifier;
|
2026-01-15 15:53:44 +00:00
|
|
|
|
|
2026-04-10 23:14:37 +00:00
|
|
|
|
try {
|
2026-01-15 15:53:44 +00:00
|
|
|
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
2026-04-10 23:14:37 +00:00
|
|
|
|
final info = await _deviceInfo.androidInfo;
|
|
|
|
|
|
// ANDROID_ID: アプリ再インストールでは変わらない。Factory Resetで変わる。
|
|
|
|
|
|
deviceIdentifier = 'android-${info.id}';
|
2026-01-15 15:53:44 +00:00
|
|
|
|
} else {
|
2026-04-10 23:14:37 +00:00
|
|
|
|
// iOS / その他: UUIDを生成して永続化
|
|
|
|
|
|
// identifierForVendor は全ベンダーアプリ削除後の再インストールで変わるため不使用
|
|
|
|
|
|
deviceIdentifier = 'uuid-${const Uuid().v4()}';
|
2026-01-15 15:53:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
2026-04-10 23:14:37 +00:00
|
|
|
|
debugPrint('[Device] Failed to get hardware ID, using UUID fallback: $e');
|
|
|
|
|
|
deviceIdentifier = 'fallback-${const Uuid().v4()}';
|
2026-01-15 15:53:44 +00:00
|
|
|
|
}
|
2026-04-10 23:14:37 +00:00
|
|
|
|
|
|
|
|
|
|
final id = sha256.convert(utf8.encode(deviceIdentifier)).toString();
|
|
|
|
|
|
await prefs.setString(_prefKey, id);
|
|
|
|
|
|
debugPrint('[Device] Generated and persisted device ID: ${id.substring(0, 8)}...');
|
|
|
|
|
|
return id;
|
2026-01-15 15:53:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// デバイス情報をリセット(テスト用)
|
2026-04-10 23:14:37 +00:00
|
|
|
|
static Future<void> reset() async {
|
2026-01-15 15:53:44 +00:00
|
|
|
|
_cachedDeviceId = null;
|
2026-04-10 23:14:37 +00:00
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.remove(_prefKey);
|
2026-01-15 15:53:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|