add authorization

This commit is contained in:
2024-11-15 18:27:59 +08:00
parent edff9c7a0c
commit 165e5f2850
13 changed files with 287 additions and 97 deletions

View File

@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<application <application
android:label="venera" android:label="venera"
android:name="${applicationName}" android:name="${applicationName}"

View File

@@ -10,10 +10,11 @@ import android.view.KeyEvent
import android.Manifest import android.Manifest
import android.os.Environment import android.os.Environment
import android.provider.Settings import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
@@ -22,82 +23,15 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.lang.Exception import java.lang.Exception
class MainActivity : FlutterActivity() { class MainActivity : FlutterFragmentActivity() {
var volumeListen = VolumeListen() var volumeListen = VolumeListen()
var listening = false var listening = false
private val pickDirectoryCode = 1
private lateinit var result: MethodChannel.Result private lateinit var result: MethodChannel.Result
private val storageRequestCode = 0x10 private val storageRequestCode = 0x10
private var storagePermissionRequest: ((Boolean) -> Unit)? = null private var storagePermissionRequest: ((Boolean) -> Unit)? = null
private val selectFileCode = 0x11
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == pickDirectoryCode) {
if(resultCode != Activity.RESULT_OK) {
result.success(null)
return
}
val pickedDirectoryUri = data?.data
if (pickedDirectoryUri == null) {
result.success(null)
return
}
Thread {
try {
result.success(onPickedDirectory(pickedDirectoryUri))
}
catch (e: Exception) {
result.error("Failed to Copy Files", e.toString(), null)
}
}.start()
} else if (requestCode == storageRequestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
storagePermissionRequest?.invoke(Environment.isExternalStorageManager())
}
storagePermissionRequest = null
} else if (requestCode == selectFileCode) {
if (resultCode != Activity.RESULT_OK) {
result.success(null)
return
}
val uri = data?.data
if (uri == null) {
result.success(null)
return
}
val contentResolver = context.contentResolver
val file = DocumentFile.fromSingleUri(context, uri)
if (file == null) {
result.success(null)
return
}
val fileName = file.name
if (fileName == null) {
result.success(null)
return
}
// copy file to cache directory
val cacheDir = context.cacheDir
val newFile = File(cacheDir, fileName)
val inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
result.success(null)
return
}
val outputStream = FileOutputStream(newFile)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
// send file path to flutter
result.success(newFile.absolutePath)
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine) GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel( MethodChannel(
@@ -115,12 +49,30 @@ class MainActivity : FlutterActivity() {
} }
res.success(null) res.success(null)
} }
"getDirectoryPath" -> { "getDirectoryPath" -> {
this.result = res
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
startActivityForResult(intent, pickDirectoryCode) val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
if (activityResult.resultCode != Activity.RESULT_OK) {
result.success(null)
} }
val pickedDirectoryUri = activityResult.data?.data
if (pickedDirectoryUri == null)
result.success(null)
else
Thread {
try {
result.success(onPickedDirectory(pickedDirectoryUri))
} catch (e: Exception) {
result.error("Failed to Copy Files", e.toString(), null)
}
}.start()
}
launcher.launch(intent)
}
else -> res.notImplemented() else -> res.notImplemented()
} }
} }
@@ -137,6 +89,7 @@ class MainActivity : FlutterActivity() {
events.success(2) events.success(2)
} }
} }
override fun onCancel(arguments: Any?) { override fun onCancel(arguments: Any?) {
listening = false listening = false
} }
@@ -144,7 +97,7 @@ class MainActivity : FlutterActivity() {
val storageChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "venera/storage") val storageChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "venera/storage")
storageChannel.setMethodCallHandler { _, res -> storageChannel.setMethodCallHandler { _, res ->
requestStoragePermission {result -> requestStoragePermission { result ->
res.success(result) res.success(result)
} }
} }
@@ -167,12 +120,13 @@ class MainActivity : FlutterActivity() {
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if(listening){ if (listening) {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> { KeyEvent.KEYCODE_VOLUME_DOWN -> {
volumeListen.down() volumeListen.down()
return true return true
} }
KeyEvent.KEYCODE_VOLUME_UP -> { KeyEvent.KEYCODE_VOLUME_UP -> {
volumeListen.up() volumeListen.up()
return true return true
@@ -184,8 +138,8 @@ class MainActivity : FlutterActivity() {
/// copy the directory to tmp directory, return copied directory /// copy the directory to tmp directory, return copied directory
private fun onPickedDirectory(uri: Uri): String { private fun onPickedDirectory(uri: Uri): String {
val contentResolver = context.contentResolver val contentResolver = contentResolver
var tmp = context.cacheDir var tmp = cacheDir
tmp = File(tmp, "getDirectoryPathTemp") tmp = File(tmp, "getDirectoryPathTemp")
tmp.mkdir() tmp.mkdir()
copyDirectory(contentResolver, uri, tmp) copyDirectory(contentResolver, uri, tmp)
@@ -194,9 +148,9 @@ class MainActivity : FlutterActivity() {
} }
private fun copyDirectory(resolver: ContentResolver, srcUri: Uri, destDir: File) { private fun copyDirectory(resolver: ContentResolver, srcUri: Uri, destDir: File) {
val src = DocumentFile.fromTreeUri(context, srcUri) ?: return val src = DocumentFile.fromTreeUri(this, srcUri) ?: return
for (file in src.listFiles()) { for (file in src.listFiles()) {
if(file.isDirectory) { if (file.isDirectory) {
val newDir = File(destDir, file.name!!) val newDir = File(destDir, file.name!!)
newDir.mkdir() newDir.mkdir()
copyDirectory(resolver, file.uri, newDir) copyDirectory(resolver, file.uri, newDir)
@@ -212,7 +166,7 @@ class MainActivity : FlutterActivity() {
} }
private fun requestStoragePermission(result: (Boolean) -> Unit) { private fun requestStoragePermission(result: (Boolean) -> Unit) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
val readPermission = ContextCompat.checkSelfPermission( val readPermission = ContextCompat.checkSelfPermission(
this, this,
Manifest.permission.READ_EXTERNAL_STORAGE Manifest.permission.READ_EXTERNAL_STORAGE
@@ -241,8 +195,11 @@ class MainActivity : FlutterActivity() {
try { try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT") intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:" + context.packageName) intent.data = Uri.parse("package:$packageName")
startActivityForResult(intent, storageRequestCode) val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
result(Environment.isExternalStorageManager())
}
launcher.launch(intent)
} catch (e: Exception) { } catch (e: Exception) {
result(false) result(false)
} }
@@ -258,7 +215,7 @@ class MainActivity : FlutterActivity() {
grantResults: IntArray grantResults: IntArray
) { ) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == storageRequestCode) { if (requestCode == storageRequestCode) {
storagePermissionRequest?.invoke(grantResults.all { storagePermissionRequest?.invoke(grantResults.all {
it == PackageManager.PERMISSION_GRANTED it == PackageManager.PERMISSION_GRANTED
}) })
@@ -266,21 +223,57 @@ class MainActivity : FlutterActivity() {
} }
} }
fun openFile() { private fun openFile() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*" intent.type = "*/*"
startActivityForResult(intent, selectFileCode) val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
if (activityResult.resultCode != Activity.RESULT_OK) {
result.success(null)
}
val uri = activityResult.data?.data
if (uri == null) {
result.success(null)
return@registerForActivityResult
}
val contentResolver = contentResolver
val file = DocumentFile.fromSingleUri(this, uri)
if (file == null) {
result.success(null)
return@registerForActivityResult
}
val fileName = file.name
if (fileName == null) {
result.success(null)
return@registerForActivityResult
}
// copy file to cache directory
val cacheDir = cacheDir
val newFile = File(cacheDir, fileName)
val inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
result.success(null)
return@registerForActivityResult
}
val outputStream = FileOutputStream(newFile)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
// send file path to flutter
result.success(newFile.absolutePath)
}
launcher.launch(intent)
} }
} }
class VolumeListen{ class VolumeListen {
var onUp = fun() {} var onUp = fun() {}
var onDown = fun() {} var onDown = fun() {}
fun up(){ fun up() {
onUp() onUp()
} }
fun down(){
fun down() {
onDown() onDown()
} }
} }

View File

@@ -210,7 +210,8 @@
"Create Folder": "新建文件夹", "Create Folder": "新建文件夹",
"Select an image on screen": "选择屏幕上的图片", "Select an image on screen": "选择屏幕上的图片",
"Added @count comics to download queue.": "已添加 @count 本漫画到下载队列", "Added @count comics to download queue.": "已添加 @count 本漫画到下载队列",
"Ignore Certificate Errors": "忽略证书错误" "Ignore Certificate Errors": "忽略证书错误",
"Authorization Required": "需要身份验证"
}, },
"zh_TW": { "zh_TW": {
"Home": "首頁", "Home": "首頁",
@@ -423,6 +424,7 @@
"Create Folder": "新建文件夾", "Create Folder": "新建文件夾",
"Select an image on screen": "選擇屏幕上的圖片", "Select an image on screen": "選擇屏幕上的圖片",
"Added @count comics to download queue.": "已添加 @count 本漫畫到下載隊列", "Added @count comics to download queue.": "已添加 @count 本漫畫到下載隊列",
"Ignore Certificate Errors": "忽略證書錯誤" "Ignore Certificate Errors": "忽略證書錯誤",
"Authorization Required": "需要身份驗證"
} }
} }

View File

@@ -51,5 +51,7 @@
<true/> <true/>
<key>LSSupportsOpeningDocumentsInPlace</key> <key>LSSupportsOpeningDocumentsInPlace</key>
<true/> <true/>
<key>NSFaceIDUsageDescription</key>
<string>Ensure that the operation is being performed by the user themselves.</string>
</dict> </dict>
</plist> </plist>

View File

@@ -120,6 +120,7 @@ class _Settings with ChangeNotifier {
'enableTurnPageByVolumeKey': true, 'enableTurnPageByVolumeKey': true,
'enableClockAndBatteryInfoInReader': true, 'enableClockAndBatteryInfoInReader': true,
'ignoreCertificateErrors': false, 'ignoreCertificateErrors': false,
'authorizationRequired': false,
}; };
operator [](String key) { operator [](String key) {

View File

@@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:rhttp/rhttp.dart'; import 'package:rhttp/rhttp.dart';
import 'package:venera/foundation/log.dart'; import 'package:venera/foundation/log.dart';
import 'package:venera/network/app_dio.dart'; import 'package:venera/pages/auth_page.dart';
import 'package:venera/pages/comic_source_page.dart'; import 'package:venera/pages/comic_source_page.dart';
import 'package:venera/pages/main_page.dart'; import 'package:venera/pages/main_page.dart';
import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/pages/settings/settings_page.dart';
@@ -65,15 +65,59 @@ class MyApp extends StatefulWidget {
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override @override
void initState() { void initState() {
checkUpdates(); checkUpdates();
App.registerForceRebuild(forceRebuild); App.registerForceRebuild(forceRebuild);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
WidgetsBinding.instance.addObserver(this);
super.initState(); super.initState();
} }
bool isAuthPageActive = false;
OverlayEntry? hideContentOverlay;
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if(!App.isMobile) {
return;
}
if (state == AppLifecycleState.inactive && hideContentOverlay == null) {
hideContentOverlay = OverlayEntry(
builder: (context) {
return Positioned.fill(
child: Container(
width: double.infinity,
height: double.infinity,
color: App.rootContext.colorScheme.surface,
),
);
},
);
Overlay.of(App.rootContext).insert(hideContentOverlay!);
} else if (hideContentOverlay != null &&
state == AppLifecycleState.resumed) {
hideContentOverlay!.remove();
hideContentOverlay = null;
}
if (state == AppLifecycleState.hidden &&
appdata.settings['authorizationRequired'] &&
!isAuthPageActive) {
isAuthPageActive = true;
App.rootContext.to(
() => AuthPage(
onSuccessfulAuth: () {
App.rootContext.pop();
isAuthPageActive = false;
},
),
);
}
super.didChangeAppLifecycleState(state);
}
void forceRebuild() { void forceRebuild() {
void rebuild(Element el) { void rebuild(Element el) {
el.markNeedsBuild(); el.markNeedsBuild();
@@ -86,14 +130,25 @@ class _MyAppState extends State<MyApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget home;
if (appdata.settings['authorizationRequired']) {
home = AuthPage(
onSuccessfulAuth: () {
App.rootContext.toReplacement(() => const MainPage());
},
);
} else {
home = const MainPage();
}
return MaterialApp( return MaterialApp(
home: const MainPage(), home: home,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: App.mainColor, seedColor: App.mainColor,
surface: Colors.white, surface: Colors.white,
primary: App.mainColor.shade600, primary: App.mainColor.shade600,
// ignore: deprecated_member_use
background: Colors.white, background: Colors.white,
), ),
fontFamily: App.isWindows ? "Microsoft YaHei" : null, fontFamily: App.isWindows ? "Microsoft YaHei" : null,
@@ -105,6 +160,7 @@ class _MyAppState extends State<MyApp> {
brightness: Brightness.dark, brightness: Brightness.dark,
surface: Colors.black, surface: Colors.black,
primary: App.mainColor.shade400, primary: App.mainColor.shade400,
// ignore: deprecated_member_use
background: Colors.black, background: Colors.black,
), ),
fontFamily: App.isWindows ? "Microsoft YaHei" : null, fontFamily: App.isWindows ? "Microsoft YaHei" : null,
@@ -171,12 +227,12 @@ class _MyAppState extends State<MyApp> {
} }
void checkUpdates() async { void checkUpdates() async {
if(!appdata.settings['checkUpdateOnStart']) { if (!appdata.settings['checkUpdateOnStart']) {
return; return;
} }
var lastCheck = appdata.implicitData['lastCheckUpdate'] ?? 0; var lastCheck = appdata.implicitData['lastCheckUpdate'] ?? 0;
var now = DateTime.now().millisecondsSinceEpoch; var now = DateTime.now().millisecondsSinceEpoch;
if(now - lastCheck < 24 * 60 * 60 * 1000) { if (now - lastCheck < 24 * 60 * 60 * 1000) {
return; return;
} }
appdata.implicitData['lastCheckUpdate'] = now; appdata.implicitData['lastCheckUpdate'] = now;

60
lib/pages/auth_page.dart Normal file
View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';
import 'package:venera/utils/translations.dart';
class AuthPage extends StatefulWidget {
const AuthPage({super.key, this.onSuccessfulAuth});
final void Function()? onSuccessfulAuth;
@override
State<AuthPage> createState() => _AuthPageState();
}
class _AuthPageState extends State<AuthPage> {
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (!didPop) {
SystemNavigator.pop();
}
},
child: Material(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.security, size: 36),
const SizedBox(height: 16),
Text("Authentication Required".tl),
const SizedBox(height: 16),
FilledButton(
onPressed: auth,
child: Text("Continue".tl),
),
],
),
),
),
);
}
void auth() async {
var localAuth = LocalAuthentication();
var canCheckBiometrics = await localAuth.canCheckBiometrics;
if (!canCheckBiometrics && !await localAuth.isDeviceSupported()) {
widget.onSuccessfulAuth?.call();
return;
}
var isAuthorized = await localAuth.authenticate(
localizedReason: "Please authenticate to continue".tl,
);
if (isAuthorized) {
widget.onSuccessfulAuth?.call();
}
}
}

View File

@@ -36,12 +36,12 @@ class _AppSettingsState extends State<AppSettings> {
if (App.isAndroid) { if (App.isAndroid) {
var channel = const MethodChannel("venera/storage"); var channel = const MethodChannel("venera/storage");
var permission = await channel.invokeMethod(''); var permission = await channel.invokeMethod('');
if(permission != true) { if (permission != true) {
context.showMessage(message: "Permission denied".tl); context.showMessage(message: "Permission denied".tl);
return; return;
} }
var path = await selectDirectory(); var path = await selectDirectory();
if(path != null) { if (path != null) {
// check if the path is writable // check if the path is writable
var testFile = File(FilePath.join(path, "test")); var testFile = File(FilePath.join(path, "test"));
try { try {
@@ -177,6 +177,29 @@ class _AppSettingsState extends State<AppSettings> {
App.forceRebuild(); App.forceRebuild();
}, },
).toSliver(), ).toSliver(),
if (!App.isLinux)
_SwitchSetting(
title: "Authorization Required".tl,
settingKey: "authorizationRequired",
onChanged: () async {
var current = appdata.settings['authorizationRequired'];
if (current) {
final auth = LocalAuthentication();
final bool canAuthenticateWithBiometrics =
await auth.canCheckBiometrics;
final bool canAuthenticate = canAuthenticateWithBiometrics ||
await auth.isDeviceSupported();
if (!canAuthenticate) {
context.showMessage(message: "Biometrics not supported".tl);
setState(() {
appdata.settings['authorizationRequired'] = false;
});
appdata.saveData();
return;
}
}
},
).toSliver(),
], ],
); );
} }

View File

@@ -33,9 +33,10 @@ class _SwitchSettingState extends State<_SwitchSetting> {
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
appdata.settings[widget.settingKey] = value; appdata.settings[widget.settingKey] = value;
appdata.saveData();
}); });
appdata.saveData().then((_) {
widget.onChanged?.call(); widget.onChanged?.call();
});
}, },
), ),
); );

View File

@@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart';
import 'package:local_auth/local_auth.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:venera/components/components.dart'; import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/app.dart';

View File

@@ -356,6 +356,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
url: "https://pub.dev"
source: hosted
version: "2.0.23"
flutter_qjs: flutter_qjs:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -512,6 +520,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
local_auth:
dependency: "direct main"
description:
name: local_auth
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
local_auth_android:
dependency: transitive
description:
name: local_auth_android
sha256: "6763aaf8965f21822624cb2fd3c03d2a8b3791037b5efb0fe4b13e110f5afc92"
url: "https://pub.dev"
source: hosted
version: "1.0.46"
local_auth_darwin:
dependency: transitive
description:
name: local_auth_darwin
sha256: "6d2950da311d26d492a89aeb247c72b4653ddc93601ea36a84924a396806d49c"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
local_auth_platform_interface:
dependency: transitive
description:
name: local_auth_platform_interface
sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a"
url: "https://pub.dev"
source: hosted
version: "1.0.10"
local_auth_windows:
dependency: transitive
description:
name: local_auth_windows
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
url: "https://pub.dev"
source: hosted
version: "1.0.11"
lodepng_flutter: lodepng_flutter:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -64,6 +64,7 @@ dependencies:
url: https://github.com/wgh136/webdav_client url: https://github.com/wgh136/webdav_client
ref: 285f87f15bccd2d5d5ff443761348c6ee47b98d1 ref: 285f87f15bccd2d5d5ff443761348c6ee47b98d1
battery_plus: ^6.2.0 battery_plus: ^6.2.0
local_auth: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -54,6 +54,7 @@ Source: "{#RootPath}\build\windows\x64\runner\Release\url_launcher_windows_plugi
Source: "{#RootPath}\build\windows\x64\runner\Release\battery_plus_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\battery_plus_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\screen_retriever_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\screen_retriever_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\window_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\window_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\local_auth_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\zip_flutter.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\zip_flutter.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\rhttp.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\rhttp.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#RootPath}\build\windows\x64\runner\Release\lodepng_flutter.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\lodepng_flutter.dll"; DestDir: "{app}"; Flags: ignoreversion