Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 68 additions & 38 deletions lib/core/installation_tracker.dart
Original file line number Diff line number Diff line change
@@ -1,50 +1,80 @@
import 'package:dpip/core/preference.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:dpip/utils/log.dart';
import 'package:uuid/uuid.dart';

final talker = TalkerManager.instance;
const _uuid = Uuid();

Future<void> initializeInstallationData() async {
// Guard future to prevent concurrent initializations from racing.
Future<void>? _initializationFuture;

final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final int? installTime = packageInfo.installTime?.millisecondsSinceEpoch;
final String currentVersion = packageInfo.version;
/// Public entry point. Multiple callers will await the same initialization
/// future so we avoid generating / writing multiple installIds concurrently.
Future<void> initializeInstallationData() {
if (_initializationFuture != null) return _initializationFuture!;
_initializationFuture = _initializeInstallationDataInternal();
return _initializationFuture!;
}

if (installTime == null) {
talker.warning('無法獲取目前的應用程式安裝時間,無法執行安裝狀態檢查。');
return;
}
Future<void> _initializeInstallationDataInternal() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
final currentVersion = packageInfo.version;
final currentBuildNumber = packageInfo.buildNumber;

final String? storedVersion = Preference.version;
final int? storedInstallTime = Preference.installTime;

if (storedInstallTime == null || storedVersion == null) {
talker.info('這是 App 首次安裝。');

Preference.instance.clear();
Preference.installTime = installTime;
Preference.version = currentVersion;
talker.info('已儲存新的安裝時間和版本,並執行了數據初始化/重置。');
} else if (storedVersion != currentVersion) {
talker.info('偵測到版本變更:$storedVersion → $currentVersion');

if (storedInstallTime != installTime) {
talker.info('安裝時間也已變更,這是版本升級。');
Preference.installTime = installTime;
String? installId;
try {
installId = await Preference.installId;
} catch (e, s) {
// Secure storage read failed; log and continue with a null installId so
// we generate a new one locally and attempt to persist it.
talker.error('Failed to read installId from secure storage', e, s);
installId = null;
}

Preference.version = currentVersion;
talker.info('已更新版本資訊,保留現有數據。');
} else if (storedInstallTime != installTime) {
talker.warning('偵測到相同版本但安裝時間不同。');
talker.log('系統安裝時間: $installTime, 儲存的安裝時間: $storedInstallTime');
talker.info('這是解除安裝後重新安裝相同版本。');

Preference.instance.clear();
Preference.installTime = installTime;
Preference.version = currentVersion;
talker.info('已執行數據重置。');
} else {
talker.info('應用程式未重新安裝也未升級。使用現有安裝資料。');

final storedVersion = Preference.version;
final storedBuildNumber = Preference.buildNumber;

if (installId == null) {
talker.info('首次安裝或資料重置,建立新的 installId');

installId = _uuid.v4();
try {
await Preference.setInstallId(installId);
} catch (e, s) {
// If we cannot write to secure storage, log the error but continue.
talker.error('Failed to write installId to secure storage', e, s);
}

Preference.version = currentVersion;
Preference.buildNumber = currentBuildNumber;
talker.info(
'已建立新的 installId 並儲存版本資訊',
'version: $currentVersion | buildNumber: $currentBuildNumber\n'
'installId: $installId'
);
return;
}

if (storedVersion != currentVersion || storedBuildNumber != currentBuildNumber) {
talker.info('偵測到版本變更:$storedVersion → $currentVersion');

if (storedBuildNumber != currentBuildNumber) {
talker.info('Build 號也已變更,這是版本升級。');
}
Preference.version = currentVersion;
Preference.buildNumber = currentBuildNumber;
talker.info('已更新版本資訊,保留現有數據。');
return;
}

talker.info(
'應用程式未重新安裝也未升級,使用現有的安裝資料。\n'
'version: $currentVersion | buildNumber: $currentBuildNumber\n'
'installId: $installId'
);
} catch (e, s) {
talker.error('初始化安裝資料時發生錯誤', e, s);
}
}
24 changes: 20 additions & 4 deletions lib/core/preference.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import 'package:i18n_extension/i18n_extension.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

import 'package:dpip/core/i18n.dart';
import 'package:dpip/utils/extensions/preference.dart';

class PreferenceKeys {
static const lastUpdateToServerTime = 'lastUpdateToServerTime';

// region App Installation
static const appVersion = 'app-version';
static const appBuildNumber = 'app-build-number';
static const installId = 'install-id';
// endregion

// #region Location
static const locationAuto = 'location:auto';
static const locationCode = 'location:code';
Expand Down Expand Up @@ -44,6 +51,7 @@ class Preference {
Preference._();

static late SharedPreferencesWithCache instance;
static const _secureStorage = FlutterSecureStorage();

static Future<void> init() async {
instance = await SharedPreferencesWithCache.create(cacheOptions: const SharedPreferencesWithCacheOptions());
Expand All @@ -54,11 +62,19 @@ class Preference {
await instance.reloadCache();
}

static String? get version => instance.getString('app-version');
static set version(String? value) => instance.set('app-version', value);
// #region App Installation
static String? get version => instance.getString(PreferenceKeys.appVersion);
static set version(String? value) => instance.set(PreferenceKeys.appVersion, value);

static String? get buildNumber => instance.getString(PreferenceKeys.appBuildNumber);
static set buildNumber(String? value) => instance.set(PreferenceKeys.appBuildNumber, value);

static int? get installTime => instance.getInt('install-time');
static set installTime(int? value) => instance.set('install-time', value);
static Future<String?> get installId => _secureStorage.read(key: PreferenceKeys.installId);
static Future<void> setInstallId(String? value) =>
value == null
? _secureStorage.delete(key: PreferenceKeys.installId)
: _secureStorage.write(key: PreferenceKeys.installId, value: value);
// #endregion

static int? get lastUpdateToServerTime => instance.getInt(PreferenceKeys.lastUpdateToServerTime);
static set lastUpdateToServerTime(int? value) => instance.set(PreferenceKeys.lastUpdateToServerTime, value);
Expand Down
24 changes: 14 additions & 10 deletions lib/core/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ class LocationService {

DartPluginRegistrant.ensureInitialized();

await Preference.init();
await AppLocalizations.load();
await LocationNameLocalizations.load();

if (Preference.locationAuto != true) {
await _$onStop();
return;
}

_$geoJsonData = await Global.loadTownGeojson();
_$locationData = await Global.loadLocationData();

_$service.setAutoStartOnBootMode(true);

await AwesomeNotifications().createNotification(
content: NotificationContent(
id: LocationServiceManager.kNotificationId,
Expand All @@ -230,16 +244,6 @@ class LocationService {
);

await _$service.setAsForegroundService();

await Preference.init();
await AppLocalizations.load();
await LocationNameLocalizations.load();

_$geoJsonData = await Global.loadTownGeojson();
_$locationData = await Global.loadLocationData();

_$service.setAutoStartOnBootMode(true);

_$service.on(LocationServiceEvent.stop).listen((_) => _$onStop());

// Start the periodic location update task
Expand Down
2 changes: 2 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dynamic_system_colors
import firebase_core
import firebase_crashlytics
import firebase_messaging
import flutter_secure_storage_darwin
import gal
import geolocator_apple
import in_app_purchase_storekit
Expand All @@ -28,6 +29,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
InAppPurchasePlugin.register(with: registry.registrar(forPlugin: "InAppPurchasePlugin"))
Expand Down
52 changes: 50 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.7+1"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: f7eceb0bc6f4fd0441e29d43cab9ac2a1c5ffd7ea7b64075136b718c46954874
url: "https://pub.dev"
source: hosted
version: "10.0.0-beta.4"
flutter_secure_storage_darwin:
dependency: transitive
description:
name: flutter_secure_storage_darwin
sha256: f226f2a572bed96bc6542198ebaec227150786e34311d455a7e2d3d06d951845
url: "https://pub.dev"
source: hosted
version: "0.1.0"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: "9b4b73127e857cd3117d43a70fa3dddadb6e0b253be62e6a6ab85caa0742182c"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: "4c3f233e739545c6cb09286eeec1cc4744138372b985113acc904f7263bef517"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: ff32af20f70a8d0e59b2938fc92de35b54a74671041c814275afd80e27df9f21
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
Expand Down Expand Up @@ -771,7 +819,7 @@ packages:
source: hosted
version: "1.0.3"
js:
dependency: transitive
dependency: "direct dev"
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
Expand Down Expand Up @@ -1472,7 +1520,7 @@ packages:
source: hosted
version: "3.1.4"
uuid:
dependency: transitive
dependency: "direct main"
description:
name: uuid
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ dependencies:
styled_text: ^8.1.0
talker_flutter: ^4.7.2
timezone: ^0.10.0
uuid: ^4.5.2
url_launcher: ^6.3.1
ip_country_lookup: ^1.0.0
flutter_icmp_ping: ^3.1.3
geojson_vi: ^2.2.5
crypto: ^3.0.6
async: ^2.13.0
flutter_secure_storage: ^10.0.0-beta.4

dev_dependencies:
build_runner: ^2.10.0
Expand Down
Loading