mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
Save preferences to same directory to binary file on Windows
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "utils"]
|
||||
path = utils
|
||||
url = https://github.com/lifegpc/c-utils
|
||||
11
lib/config/base.dart
Normal file
11
lib/config/base.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
abstract interface class Config {
|
||||
Future<bool> clear();
|
||||
bool containsKey(String key);
|
||||
Object? get(String key);
|
||||
String? getString(String key);
|
||||
Future<bool> setString(String key, String value);
|
||||
int? getInt(String key);
|
||||
Future<bool> setInt(String key, int value);
|
||||
bool? getBool(String key);
|
||||
Future<bool> setBool(String key, bool value);
|
||||
}
|
||||
43
lib/config/shared_preferences.dart
Normal file
43
lib/config/shared_preferences.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'base.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SharedPreferencesConfig implements Config {
|
||||
SharedPreferencesConfig(this._prefs);
|
||||
final SharedPreferences _prefs;
|
||||
@override
|
||||
Future<bool> clear() {
|
||||
return _prefs.clear();
|
||||
}
|
||||
@override
|
||||
bool containsKey(String key) {
|
||||
return _prefs.containsKey(key);
|
||||
}
|
||||
@override
|
||||
Object? get(String key) {
|
||||
return _prefs.get(key);
|
||||
}
|
||||
@override
|
||||
String? getString(String key) {
|
||||
return _prefs.getString(key);
|
||||
}
|
||||
@override
|
||||
Future<bool> setString(String key, String value) {
|
||||
return _prefs.setString(key, value);
|
||||
}
|
||||
@override
|
||||
int? getInt(String key) {
|
||||
return _prefs.getInt(key);
|
||||
}
|
||||
@override
|
||||
Future<bool> setInt(String key, int value) {
|
||||
return _prefs.setInt(key, value);
|
||||
}
|
||||
@override
|
||||
bool? getBool(String key) {
|
||||
return _prefs.getBool(key);
|
||||
}
|
||||
@override
|
||||
Future<bool> setBool(String key, bool value) {
|
||||
return _prefs.setBool(key, value);
|
||||
}
|
||||
}
|
||||
108
lib/config/windows.dart
Normal file
108
lib/config/windows.dart
Normal file
@@ -0,0 +1,108 @@
|
||||
import 'dart:convert' show json;
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/local.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'base.dart';
|
||||
import '../globals.dart';
|
||||
|
||||
final _log = Logger("WindowsConfig");
|
||||
|
||||
class WindowsConfig implements Config {
|
||||
WindowsConfig();
|
||||
FileSystem fs = const LocalFileSystem();
|
||||
Map<String, Object>? _cachedPreferences;
|
||||
File? _filePath;
|
||||
Future<File?> filePath() async {
|
||||
if (_filePath != null) return _filePath;
|
||||
final String? exe = await platformPath.getCurrentExe();
|
||||
if (exe == null) return null;
|
||||
final dir = path.dirname(exe);
|
||||
final name = path.basenameWithoutExtension(exe);
|
||||
return _filePath = fs.file(path.join(dir, "$name.json"));
|
||||
}
|
||||
|
||||
Future<Map<String, Object>> reload() async {
|
||||
Map<String, Object> preferences = <String, Object>{};
|
||||
final File? localDataFile = await filePath();
|
||||
if (localDataFile != null && localDataFile.existsSync()) {
|
||||
final String stringMap = localDataFile.readAsStringSync();
|
||||
if (stringMap.isNotEmpty) {
|
||||
final Object? data = json.decode(stringMap);
|
||||
if (data is Map) {
|
||||
preferences = data.cast<String, Object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
_cachedPreferences = preferences;
|
||||
return preferences;
|
||||
}
|
||||
|
||||
Future<Map<String, Object>> _readPreferences() async {
|
||||
return _cachedPreferences ?? await reload();
|
||||
}
|
||||
|
||||
Future<bool> _writePreferences(Map<String, Object> preferences) async {
|
||||
try {
|
||||
final File? localDataFile = await filePath();
|
||||
if (localDataFile == null) {
|
||||
_log.warning('Unable to determine where to write preferences.');
|
||||
return false;
|
||||
}
|
||||
if (!localDataFile.existsSync()) {
|
||||
localDataFile.createSync(recursive: true);
|
||||
}
|
||||
final String stringMap = json.encode(preferences);
|
||||
localDataFile.writeAsStringSync(stringMap);
|
||||
} catch (e) {
|
||||
_log.severe('Error saving preferences to disk: ', e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> clear() async {
|
||||
final Map<String, Object> preferences = await _readPreferences();
|
||||
preferences.clear();
|
||||
return _writePreferences(preferences);
|
||||
}
|
||||
|
||||
Future<bool> setValue(String key, Object value) async {
|
||||
final Map<String, Object> preferences = await _readPreferences();
|
||||
preferences[key] = value;
|
||||
return _writePreferences(preferences);
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsKey(String key) {
|
||||
return _cachedPreferences?.containsKey(key) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Object? get(String key) => _cachedPreferences?[key];
|
||||
|
||||
@override
|
||||
String? getString(String key) => _cachedPreferences?[key] as String?;
|
||||
|
||||
@override
|
||||
Future<bool> setString(String key, String value) {
|
||||
return setValue(key, value);
|
||||
}
|
||||
|
||||
@override
|
||||
int? getInt(String key) => _cachedPreferences?[key] as int?;
|
||||
|
||||
@override
|
||||
Future<bool> setInt(String key, int value) {
|
||||
return setValue(key, value);
|
||||
}
|
||||
|
||||
@override
|
||||
bool? getBool(String key) => _cachedPreferences?[key] as bool?;
|
||||
|
||||
@override
|
||||
Future<bool> setBool(String key, bool value) {
|
||||
return setValue(key, value);
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,18 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'api/client.dart';
|
||||
import 'auth.dart';
|
||||
import 'config/base.dart';
|
||||
import 'config/shared_preferences.dart';
|
||||
import 'config/windows.dart';
|
||||
import 'platform/path.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
final dio = Dio()
|
||||
..options.validateStatus = (int? _) {
|
||||
return true;
|
||||
}
|
||||
..options.extra['withCredentials'] = true;
|
||||
SharedPreferences? _prefs;
|
||||
Config? _prefs;
|
||||
EHApi? _api;
|
||||
|
||||
Future<void> prepareJar() async {
|
||||
@@ -28,10 +33,20 @@ Future<void> prepareJar() async {
|
||||
}
|
||||
|
||||
Future<void> preparePrefs() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
if (isWindows) {
|
||||
try {
|
||||
var tmp = WindowsConfig();
|
||||
tmp.reload();
|
||||
_prefs = tmp;
|
||||
return;
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
_prefs = SharedPreferencesConfig(await SharedPreferences.getInstance());
|
||||
}
|
||||
|
||||
SharedPreferences get prefs {
|
||||
Config get prefs {
|
||||
if (_prefs == null) {
|
||||
throw Exception('SharedPreferences not initialized');
|
||||
}
|
||||
@@ -77,3 +92,4 @@ EHApi get api {
|
||||
}
|
||||
|
||||
final AuthInfo auth = AuthInfo();
|
||||
final Path platformPath = Path();
|
||||
|
||||
@@ -42,6 +42,7 @@ Future<void> initLogger() async {
|
||||
}
|
||||
|
||||
void main() async {
|
||||
if (!kIsWeb) WidgetsFlutterBinding.ensureInitialized();
|
||||
bool? usePathUrl = const bool.fromEnvironment("usePathUrl");
|
||||
if (usePathUrl == true && kIsWeb) {
|
||||
usePathUrlStrategy();
|
||||
@@ -49,7 +50,6 @@ void main() async {
|
||||
if (!kIsWeb) await prepareJar();
|
||||
await preparePrefs();
|
||||
if (isDesktop) {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await windowManager.ensureInitialized();
|
||||
}
|
||||
await initLogger();
|
||||
|
||||
28
lib/platform/path.dart
Normal file
28
lib/platform/path.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
final Logger _log = Logger("platformPath");
|
||||
|
||||
class Path {
|
||||
static const platform = MethodChannel("lifegpc.eh_downloader_flutter/path");
|
||||
String? _currentExe;
|
||||
bool _currentExeLoaded = false;
|
||||
|
||||
String? get currentExe => _currentExe;
|
||||
|
||||
Future<String?> getCurrentExe() async {
|
||||
if (_currentExeLoaded) return _currentExe;
|
||||
try {
|
||||
final String result = await platform.invokeMethod("getCurrentExe");
|
||||
_currentExe = result;
|
||||
} on PlatformException catch (e) {
|
||||
if (isWindows) {
|
||||
_log.warning("Failed to get current exe path:", e);
|
||||
}
|
||||
}
|
||||
_currentExeLoaded = true;
|
||||
return _currentExe;
|
||||
}
|
||||
}
|
||||
@@ -234,7 +234,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
@@ -438,7 +438,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies:
|
||||
cryptography_flutter: ^2.3.0
|
||||
dio: ^5.3.2
|
||||
dio_cookie_manager: ^3.1.0+1
|
||||
file: ^6.1.4
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_hooks: ^0.20.0
|
||||
@@ -21,6 +22,7 @@ dependencies:
|
||||
intl: any
|
||||
json_annotation: ^4.8.1
|
||||
logging: ^1.2.0
|
||||
path: ^1.8.3
|
||||
path_provider: ^2.1.0
|
||||
retrofit: ^4.0.1
|
||||
shared_preferences: ^2.2.0
|
||||
|
||||
1
utils
Submodule
1
utils
Submodule
Submodule utils added at 63992e1238
@@ -1,6 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
set(ENABLE_ICONV OFF CACHE BOOL "Libiconv is not needed.")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../utils" "${CMAKE_BINARY_DIR}/utils")
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../utils")
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME in the
|
||||
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
|
||||
# work.
|
||||
@@ -34,6 +38,7 @@ target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||
# dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE utils)
|
||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
#include "flutter_window.h"
|
||||
#include <flutter/event_channel.h>
|
||||
#include <flutter/event_sink.h>
|
||||
#include <flutter/event_stream_handler_functions.h>
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
#include "wchar_util.h"
|
||||
|
||||
#define MAX_PATH_SIZE 32768
|
||||
|
||||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||
: project_(project) {}
|
||||
|
||||
@@ -25,6 +35,30 @@ bool FlutterWindow::OnCreate() {
|
||||
return false;
|
||||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
|
||||
flutter::MethodChannel<> channel(
|
||||
flutter_controller_->engine()->messenger(), "lifegpc.eh_downloader_flutter/path",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
channel.SetMethodCallHandler(
|
||||
[](const flutter::MethodCall<>& call,
|
||||
std::unique_ptr<flutter::MethodResult<>> result) {
|
||||
if (call.method_name() == "getCurrentExe") {
|
||||
std::string current;
|
||||
wchar_t tmp[MAX_PATH_SIZE];
|
||||
if (!GetModuleFileNameW(nullptr, tmp, MAX_PATH_SIZE)) {
|
||||
result->Error("UNAVAILABLE", "Failed to get module file name.");
|
||||
return;
|
||||
}
|
||||
if (!wchar_util::wstr_to_str(current, tmp, CP_UTF8)) {
|
||||
result->Error("UNAVAILABLE", "Failed to convert module file name to UTF-8.");
|
||||
return;
|
||||
}
|
||||
result->Success(current);
|
||||
} else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
});
|
||||
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
|
||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||
|
||||
Reference in New Issue
Block a user