mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-20 05:19:28 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7071e34a74 | ||
|
6a73ade359 | ||
|
6c5e5043a4 | ||
|
5edaf1306d | ||
|
5bf7fdb94e | ||
|
7808bc5ccb | ||
|
06a079e452 | ||
|
de509737e6 | ||
|
08a3ba8d13 | ||
|
2b27902d5f | ||
|
62185127c2 | ||
|
9c46e3f88c | ||
|
daa4de921d | ||
|
bc977e2a5a | ||
|
1e3815ca20 | ||
|
0e2fa96b9f | ||
|
389aebe54e | ||
|
fbfeaf2a91 | ||
|
485812d076 | ||
|
68e98ec719 | ||
|
cbe41de734 | ||
|
abb8641105 |
2
.flutter
2
.flutter
Submodule .flutter updated: 54e66469a9...a14f74ff3a
@@ -113,7 +113,7 @@
|
||||
"dark": "تاریک",
|
||||
"light": "روشن",
|
||||
"followSystem": "هماهنگ با سیستم",
|
||||
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
|
||||
"followSystemThemeExplanation": "دنبال کردن تم سیستم فقط با استفاده از برنامه های شخص ثالث امکان پذیر است",
|
||||
"useBlackTheme": "استفاده از تم تیره سیاه خالص",
|
||||
"appSortBy": "مرتب سازی برنامه بر اساس",
|
||||
"authorName": "سازنده/اسم",
|
||||
@@ -147,10 +147,10 @@
|
||||
"noNewUpdates": "به روز رسانی جدیدی وجود ندارد.",
|
||||
"xHasAnUpdate": "{} یک به روز رسانی دارد.",
|
||||
"appsUpdated": "برنامه ها به روز شدند",
|
||||
"appsNotUpdated": "Failed to update applications",
|
||||
"appsNotUpdated": "به روز رسانی برنامه ها ناموفق بود",
|
||||
"appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است",
|
||||
"xWasUpdatedToY": "{} به {} به روز شد.",
|
||||
"xWasNotUpdatedToY": "Failed to update {} to {}.",
|
||||
"xWasNotUpdatedToY": "به روز رسانی {} به {} انجام نشد.",
|
||||
"errorCheckingUpdates": "خطا در بررسی بهروزرسانیها",
|
||||
"errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی بهروزرسانی پسزمینه ناموفق است نشان میدهد",
|
||||
"appsRemoved": "برنامه ها حذف شدند",
|
||||
@@ -189,9 +189,9 @@
|
||||
"disableVersionDetection": "غیرفعال کردن تشخیص نسخه",
|
||||
"noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.",
|
||||
"downloadingX": "در حال دانلود {}",
|
||||
"downloadX": "Download {}",
|
||||
"downloadedX": "Downloaded {}",
|
||||
"releaseAsset": "Release Asset",
|
||||
"downloadX": "دانلود {}",
|
||||
"downloadedX": "دانلود شده {}",
|
||||
"releaseAsset": "انتشار دارایی",
|
||||
"downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند",
|
||||
"noAPKFound": "APK پیدا نشد فایل",
|
||||
"noVersionDetection": "بدون تشخیص نسخه",
|
||||
@@ -305,13 +305,13 @@
|
||||
"installed": "نصب شده است",
|
||||
"latest": "آخرین",
|
||||
"invertRegEx": "معکوس کردن عبارت منظم",
|
||||
"note": "Note",
|
||||
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
|
||||
"badDownload": "The APK could not be parsed (incompatible or partial download)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"wiki": "Help/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
|
||||
"note": "یادداشت",
|
||||
"selfHostedNote": "از منوی کرکره ای \"{}\" می توان برای دسترسی به نمونه های خود میزبانی/سفارشی از هر منبعی استفاده کرد.",
|
||||
"badDownload": "APK قابل تجزیه نیست (دانلود ناسازگار یا جزئی)",
|
||||
"beforeNewInstallsShareToAppVerifier": "اشتراکگذاری برنامههای جدید با AppVerifier (در صورت وجود)",
|
||||
"appVerifierInstructionToast": "در AppVerifier به اشتراک بگذارید، سپس پس از آماده شدن به اینجا برگردید.",
|
||||
"wiki": "راهنما/ویکی",
|
||||
"crowdsourcedConfigsLabel": "تنظیمات برنامه Crowdsourced (با مسئولیت خود استفاده کنید)",
|
||||
"removeAppQuestion": {
|
||||
"one": "برنامه حذف شود؟",
|
||||
"other": "برنامه ها حذف شوند؟"
|
||||
@@ -361,8 +361,8 @@
|
||||
"other": "{} و {} برنامه دیگر به روز شدند."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Failed to update {} and 1 more app.",
|
||||
"other": "Failed to update {} and {} more apps."
|
||||
"one": "{} و 1 برنامه دیگر به روز نشد.",
|
||||
"other": "{} و {} برنامه دیگر به روز نشد."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
|
||||
|
@@ -22,9 +22,9 @@
|
||||
"requiredInBrackets": "(Yêu cầu)",
|
||||
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
|
||||
"colour": "Màu sắc",
|
||||
"standard": "Standard",
|
||||
"custom": "Custom",
|
||||
"useMaterialYou": "Use Material You",
|
||||
"standard": "Mặc định",
|
||||
"custom": "Tùy chỉnh",
|
||||
"useMaterialYou": "Sử dụng Material You",
|
||||
"githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub",
|
||||
"uname": "Tên người dùng",
|
||||
"wrongArgNum": "Số lượng đối số được cung cấp sai",
|
||||
@@ -147,10 +147,10 @@
|
||||
"noNewUpdates": "Không có bản cập nhật mới.",
|
||||
"xHasAnUpdate": "{} có bản cập nhật.",
|
||||
"appsUpdated": "Ứng dụng đã cập nhật ",
|
||||
"appsNotUpdated": "Failed to update applications",
|
||||
"appsNotUpdated": "Ứng dụng đã cập nhật không thành công",
|
||||
"appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền",
|
||||
"xWasUpdatedToY": "{} đã được cập nhật thành {}.",
|
||||
"xWasNotUpdatedToY": "Failed to update {} to {}.",
|
||||
"xWasNotUpdatedToY": "{} đã cập nhật thành {} không thành công.",
|
||||
"errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật",
|
||||
"errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công",
|
||||
"appsRemoved": "Ứng dụng đã loại bỏ",
|
||||
@@ -189,8 +189,8 @@
|
||||
"disableVersionDetection": "Tắt tính năng phát hiện phiên bản",
|
||||
"noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.",
|
||||
"downloadingX": "Đang tải xuống {}",
|
||||
"downloadX": "Download {}",
|
||||
"downloadedX": "Downloaded {}",
|
||||
"downloadX": "Tải xuống {}",
|
||||
"downloadedX": "Đã tải xuống {}",
|
||||
"releaseAsset": "Release Asset",
|
||||
"downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng",
|
||||
"noAPKFound": "Không tìm thấy APK",
|
||||
@@ -288,10 +288,10 @@
|
||||
"supportFixedAPKURL": "Hỗ trợ URL APK cố định",
|
||||
"selectX": "Lựa chọn {}",
|
||||
"parallelDownloads": "Cho phép tải đa luồng",
|
||||
"useShizuku": "Use Shizuku or Sui to install",
|
||||
"useShizuku": "Sử dụng Shizuku hoặc Sui để cài đặt",
|
||||
"shizukuBinderNotFound": "Shizuku chưa khởi động",
|
||||
"shizukuOld": "Old Shizuku version (<11) - update it",
|
||||
"shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead",
|
||||
"shizukuOld": "Phiên bản Shizuku lỗi thời (<11) - hãy cập nhật nó",
|
||||
"shizukuOldAndroidWithADB": "Shizuku chạy trên Android < 8.1 với ADB - hãy cập nhật Android hoặc thay bằng Sui",
|
||||
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
|
||||
"useSystemFont": "Sử dụng phông chữ hệ thống",
|
||||
"useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện",
|
||||
@@ -310,7 +310,7 @@
|
||||
"badDownload": "Không thể phân tích cú pháp APK (tải xuống một phần hoặc không tương thích)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Chia sẻ ứng dụng mới với AppVerifier (nếu có)",
|
||||
"appVerifierInstructionToast": "Chia sẻ lên AppVerifier, sau đó quay lại đây khi sẵn sàng.",
|
||||
"wiki": "Help/Wiki",
|
||||
"wiki": "Trợ giúp/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Gỡ ứng dụng?",
|
||||
@@ -361,8 +361,8 @@
|
||||
"other": "{} và {} ứng dụng khác đã được cập nhật."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Failed to update {} and 1 more app.",
|
||||
"other": "Failed to update {} and {} more apps."
|
||||
"one": "{} và 1 ứng dụng khác đã cập nhật không thành công.",
|
||||
"other": "{} và {} ứng dụng khác đã cập nhật không thảnh công."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} và 1 ứng dụng khác có thể đã được cập nhật.",
|
||||
|
@@ -111,6 +111,14 @@ class GitLab extends AppSource {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||
Map<String, dynamic> additionalSettings) async {
|
||||
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
|
||||
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
|
||||
return '$apkUrl?$optionalAuth';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<APKDetails> getLatestAPKDetails(
|
||||
String standardUrl,
|
||||
|
@@ -73,21 +73,23 @@ class HuaweiAppGallery extends AppSource {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
String appId = appIdFromRedirectDlUrl(res.headers['location']!);
|
||||
if (appId.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var relDateStr =
|
||||
res.headers['location']?.split('?')[0].split('.').reversed.toList()[1];
|
||||
var relDateStrAdj = relDateStr?.split('');
|
||||
var tempLen = relDateStrAdj?.length ?? 0;
|
||||
var i = 2;
|
||||
while (i < tempLen) {
|
||||
relDateStrAdj?.insert((i + i ~/ 2 - 1), '-');
|
||||
i += 2;
|
||||
}
|
||||
var relDate = relDateStrAdj == null
|
||||
? null
|
||||
: DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
|
||||
if (relDateStr == null) {
|
||||
if (relDateStr == null || relDateStr.length != 10) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var relDateStrAdj = relDateStr.split('');
|
||||
var tempLen = relDateStrAdj.length;
|
||||
var i = 2;
|
||||
while (i < tempLen) {
|
||||
relDateStrAdj.insert((i + i ~/ 2 - 1), '-');
|
||||
i += 2;
|
||||
}
|
||||
var relDate =
|
||||
DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
|
||||
return APKDetails(
|
||||
relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId),
|
||||
releaseDate: relDate);
|
||||
|
@@ -224,7 +224,7 @@ class _ObtainiumState extends State<Obtainium> {
|
||||
// set the background and surface colors to pure black in the amoled theme
|
||||
if (settingsProvider.useBlackTheme) {
|
||||
darkColorScheme = darkColorScheme
|
||||
.copyWith(background: Colors.black, surface: Colors.black)
|
||||
.copyWith(surface: Colors.black)
|
||||
.harmonized();
|
||||
}
|
||||
|
||||
|
@@ -226,18 +226,26 @@ class _AppPageState extends State<AppPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
app?.icon != null
|
||||
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
GestureDetector(
|
||||
child: Image.memory(
|
||||
app!.icon!,
|
||||
height: 150,
|
||||
gaplessPlayback: true,
|
||||
),
|
||||
onTap: () => pm.openApp(app.app.id),
|
||||
)
|
||||
])
|
||||
: Container(),
|
||||
FutureBuilder(
|
||||
future: appsProvider.updateAppIcon(app?.app.id),
|
||||
builder: (ctx, val) {
|
||||
return app?.icon != null
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: app == null
|
||||
? null
|
||||
: () => pm.openApp(app.app.id),
|
||||
child: Image.memory(
|
||||
app!.icon!,
|
||||
height: 150,
|
||||
gaplessPlayback: true,
|
||||
),
|
||||
)
|
||||
])
|
||||
: Container();
|
||||
}),
|
||||
const SizedBox(
|
||||
height: 25,
|
||||
),
|
||||
@@ -286,7 +294,7 @@ class _AppPageState extends State<AppPage> {
|
||||
? WebViewWidget(
|
||||
controller: WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(Theme.of(context).colorScheme.background)
|
||||
..setBackgroundColor(Theme.of(context).colorScheme.surface)
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
|
@@ -354,7 +354,11 @@ class AppsPageState extends State<AppsPage> {
|
||||
SliverFillRemaining(
|
||||
child: Center(
|
||||
child: Text(
|
||||
appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'),
|
||||
appsProvider.apps.isEmpty
|
||||
? appsProvider.loadingApps
|
||||
? tr('pleaseWait')
|
||||
: tr('noApps')
|
||||
: tr('noAppsForFilter'),
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
textAlign: TextAlign.center,
|
||||
))),
|
||||
@@ -402,29 +406,36 @@ class AppsPageState extends State<AppsPage> {
|
||||
}
|
||||
|
||||
getAppIcon(int appIndex) {
|
||||
return listedApps[appIndex].icon != null
|
||||
? Image.memory(
|
||||
listedApps[appIndex].icon!,
|
||||
gaplessPlayback: true,
|
||||
)
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationZ(0.31),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Image(
|
||||
image: const AssetImage(
|
||||
'assets/graphics/icon_small.png'),
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
gaplessPlayback: true,
|
||||
),
|
||||
)),
|
||||
]);
|
||||
return FutureBuilder(
|
||||
future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
|
||||
builder: (ctx, val) {
|
||||
return listedApps[appIndex].icon != null
|
||||
? Image.memory(
|
||||
listedApps[appIndex].icon!,
|
||||
gaplessPlayback: true,
|
||||
)
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationZ(0.31),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Image(
|
||||
image: const AssetImage(
|
||||
'assets/graphics/icon_small.png'),
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.dark
|
||||
? Colors.white.withOpacity(0.4)
|
||||
: Colors.white.withOpacity(0.3),
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
gaplessPlayback: true,
|
||||
),
|
||||
)),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
getVersionText(int appIndex) {
|
||||
@@ -503,7 +514,7 @@ class AppsPageState extends State<AppsPage> {
|
||||
);
|
||||
|
||||
var transparent =
|
||||
Theme.of(context).colorScheme.background.withAlpha(0).value;
|
||||
Theme.of(context).colorScheme.surface.withAlpha(0).value;
|
||||
List<double> stops = [
|
||||
...listedApps[index].app.categories.asMap().entries.map(
|
||||
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
|
||||
|
@@ -103,7 +103,7 @@ class _HomePageState extends State<HomePage> {
|
||||
}) !=
|
||||
null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
var appsProvider = await context.read<AppsProvider>();
|
||||
var appsProvider = context.read<AppsProvider>();
|
||||
var result = await appsProvider.import(action == 'app'
|
||||
? '{ "apps": [$dataStr] }'
|
||||
: '{ "apps": $dataStr }');
|
||||
|
@@ -33,7 +33,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
||||
var settingsProvider = context.watch<SettingsProvider>();
|
||||
|
||||
var outlineButtonStyle = ButtonStyle(
|
||||
shape: MaterialStateProperty.all(
|
||||
shape: WidgetStateProperty.all(
|
||||
StadiumBorder(
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
|
@@ -5,6 +5,7 @@ import 'package:flex_color_picker/flex_color_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:obtainium/components/custom_app_bar.dart';
|
||||
import 'package:obtainium/components/generated_form.dart';
|
||||
import 'package:obtainium/components/generated_form_modal.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/main.dart';
|
||||
import 'package:obtainium/providers/apps_provider.dart';
|
||||
@@ -945,6 +946,25 @@ class _LogsDialogState extends State<LogsDialog> {
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
var cont = (await showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext ctx) {
|
||||
return GeneratedFormModal(
|
||||
title: tr('appLogs'),
|
||||
items: const [],
|
||||
initValid: true,
|
||||
message: tr('removeFromObtainium'),
|
||||
);
|
||||
})) !=
|
||||
null;
|
||||
if (cont) {
|
||||
logsProvider.clear();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Text(tr('remove'))),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
|
@@ -329,6 +329,10 @@ Future<Map<String, String>> getHeaders(String url,
|
||||
return returnHeaders;
|
||||
}
|
||||
|
||||
Future<List<PackageInfo>> getAllInstalledInfo() async {
|
||||
return await pm.getInstalledPackages() ?? [];
|
||||
}
|
||||
|
||||
Future<PackageInfo?> getInstalledInfo(String? packageName,
|
||||
{bool printErr = true}) async {
|
||||
if (packageName != null) {
|
||||
@@ -364,7 +368,9 @@ class AppsProvider with ChangeNotifier {
|
||||
foregroundStream = FGBGEvents.stream.asBroadcastStream();
|
||||
foregroundSubscription = foregroundStream?.listen((event) async {
|
||||
isForeground = event == FGBGType.foreground;
|
||||
if (isForeground) loadApps();
|
||||
if (isForeground) {
|
||||
await loadApps();
|
||||
}
|
||||
});
|
||||
() async {
|
||||
await settingsProvider.initializeSettings();
|
||||
@@ -421,7 +427,8 @@ class AppsProvider with ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<Object> downloadApp(App app, BuildContext? context,
|
||||
{NotificationsProvider? notificationsProvider}) async {
|
||||
{NotificationsProvider? notificationsProvider,
|
||||
bool useExisting = true}) async {
|
||||
var notifId = DownloadNotification(app.finalName, 0).id;
|
||||
if (apps[app.id] != null) {
|
||||
apps[app.id]!.downloadProgress = 0;
|
||||
@@ -453,7 +460,7 @@ class AppsProvider with ChangeNotifier {
|
||||
notificationsProvider?.notify(notif);
|
||||
}
|
||||
prevProg = prog;
|
||||
}, APKDir.path);
|
||||
}, APKDir.path, useExisting: useExisting);
|
||||
// Set to 90 for remaining steps, will make null in 'finally'
|
||||
if (apps[app.id] != null) {
|
||||
apps[app.id]!.downloadProgress = -1;
|
||||
@@ -766,7 +773,8 @@ class AppsProvider with ChangeNotifier {
|
||||
Future<List<String>> downloadAndInstallLatestApps(
|
||||
List<String> appIds, BuildContext? context,
|
||||
{NotificationsProvider? notificationsProvider,
|
||||
bool forceParallelDownloads = false}) async {
|
||||
bool forceParallelDownloads = false,
|
||||
bool useExisting = true}) async {
|
||||
notificationsProvider =
|
||||
notificationsProvider ?? context?.read<NotificationsProvider>();
|
||||
List<String> appsToInstall = [];
|
||||
@@ -818,21 +826,82 @@ class AppsProvider with ChangeNotifier {
|
||||
appsToInstall =
|
||||
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
|
||||
|
||||
Future<String> updateFn(String id, {bool skipInstalls = false}) async {
|
||||
Future<void> installFn(String id, bool willBeSilent,
|
||||
DownloadedApk? downloadedFile, DownloadedXApkDir? downloadedDir) async {
|
||||
apps[id]?.downloadProgress = -1;
|
||||
notifyListeners();
|
||||
try {
|
||||
bool sayInstalled = true;
|
||||
var contextIfNewInstall =
|
||||
apps[id]?.installedInfo == null ? context : null;
|
||||
bool needBGWorkaround =
|
||||
willBeSilent && context == null && !settingsProvider.useShizuku;
|
||||
if (downloadedFile != null) {
|
||||
if (needBGWorkaround) {
|
||||
// ignore: use_build_context_synchronously
|
||||
installApk(downloadedFile, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
// ignore: use_build_context_synchronously
|
||||
sayInstalled = await installApk(downloadedFile, contextIfNewInstall,
|
||||
shizukuPretendToBeGooglePlay: apps[id]!
|
||||
.app
|
||||
.additionalSettings['shizukuPretendToBeGooglePlay'] ==
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
if (needBGWorkaround) {
|
||||
// ignore: use_build_context_synchronously
|
||||
installXApkDir(downloadedDir!, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
// ignore: use_build_context_synchronously
|
||||
sayInstalled = await installXApkDir(
|
||||
downloadedDir!, contextIfNewInstall,
|
||||
shizukuPretendToBeGooglePlay: apps[id]!
|
||||
.app
|
||||
.additionalSettings['shizukuPretendToBeGooglePlay'] ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
if (willBeSilent && context == null) {
|
||||
if (!settingsProvider.useShizuku) {
|
||||
notificationsProvider?.notify(SilentUpdateAttemptNotification(
|
||||
[apps[id]!.app],
|
||||
id: id.hashCode));
|
||||
} else {
|
||||
notificationsProvider?.notify(SilentUpdateNotification(
|
||||
[apps[id]!.app], sayInstalled,
|
||||
id: id.hashCode));
|
||||
}
|
||||
}
|
||||
if (sayInstalled) {
|
||||
installedIds.add(id);
|
||||
}
|
||||
} finally {
|
||||
apps[id]?.downloadProgress = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<Object?, Object?>> downloadFn(String id,
|
||||
{bool skipInstalls = false}) async {
|
||||
bool willBeSilent = false;
|
||||
DownloadedApk? downloadedFile;
|
||||
DownloadedXApkDir? downloadedDir;
|
||||
try {
|
||||
var downloadedArtifact =
|
||||
// ignore: use_build_context_synchronously
|
||||
await downloadApp(apps[id]!.app, context,
|
||||
notificationsProvider: notificationsProvider);
|
||||
DownloadedApk? downloadedFile;
|
||||
DownloadedXApkDir? downloadedDir;
|
||||
notificationsProvider: notificationsProvider,
|
||||
useExisting: useExisting);
|
||||
if (downloadedArtifact is DownloadedApk) {
|
||||
downloadedFile = downloadedArtifact;
|
||||
} else {
|
||||
downloadedDir = downloadedArtifact as DownloadedXApkDir;
|
||||
}
|
||||
id = downloadedFile?.appId ?? downloadedDir!.appId;
|
||||
bool willBeSilent = await canInstallSilently(apps[id]!.app);
|
||||
willBeSilent = await canInstallSilently(apps[id]!.app);
|
||||
if (!settingsProvider.useShizuku) {
|
||||
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
|
||||
throw ObtainiumError(tr('cancelled'));
|
||||
@@ -853,80 +922,33 @@ class AppsProvider with ChangeNotifier {
|
||||
// ignore: use_build_context_synchronously
|
||||
await waitForUserToReturnToForeground(context);
|
||||
}
|
||||
apps[id]?.downloadProgress = -1;
|
||||
notifyListeners();
|
||||
try {
|
||||
if (!skipInstalls) {
|
||||
bool sayInstalled = true;
|
||||
var contextIfNewInstall =
|
||||
apps[id]?.installedInfo == null ? context : null;
|
||||
bool needBGWorkaround =
|
||||
willBeSilent && context == null && !settingsProvider.useShizuku;
|
||||
if (downloadedFile != null) {
|
||||
if (needBGWorkaround) {
|
||||
// ignore: use_build_context_synchronously
|
||||
installApk(downloadedFile, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
// ignore: use_build_context_synchronously
|
||||
sayInstalled = await installApk(
|
||||
downloadedFile, contextIfNewInstall,
|
||||
shizukuPretendToBeGooglePlay:
|
||||
apps[id]!.app.additionalSettings[
|
||||
'shizukuPretendToBeGooglePlay'] ==
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
if (needBGWorkaround) {
|
||||
// ignore: use_build_context_synchronously
|
||||
installXApkDir(downloadedDir!, contextIfNewInstall,
|
||||
needsBGWorkaround: true);
|
||||
} else {
|
||||
// ignore: use_build_context_synchronously
|
||||
sayInstalled = await installXApkDir(
|
||||
downloadedDir!, contextIfNewInstall,
|
||||
shizukuPretendToBeGooglePlay:
|
||||
apps[id]!.app.additionalSettings[
|
||||
'shizukuPretendToBeGooglePlay'] ==
|
||||
true);
|
||||
}
|
||||
}
|
||||
if (willBeSilent && context == null) {
|
||||
if (!settingsProvider.useShizuku) {
|
||||
notificationsProvider?.notify(SilentUpdateAttemptNotification(
|
||||
[apps[id]!.app],
|
||||
id: id.hashCode));
|
||||
} else {
|
||||
notificationsProvider?.notify(SilentUpdateNotification(
|
||||
[apps[id]!.app], sayInstalled,
|
||||
id: id.hashCode));
|
||||
}
|
||||
}
|
||||
if (sayInstalled) {
|
||||
installedIds.add(id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
apps[id]?.downloadProgress = null;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
errors.add(id, e, appName: apps[id]?.name);
|
||||
}
|
||||
return id;
|
||||
return {
|
||||
'id': id,
|
||||
'willBeSilent': willBeSilent,
|
||||
'downloadedFile': downloadedFile,
|
||||
'downloadedDir': downloadedDir
|
||||
};
|
||||
}
|
||||
|
||||
List<Map<Object?, Object?>> downloadResults = [];
|
||||
if (forceParallelDownloads || !settingsProvider.parallelDownloads) {
|
||||
for (var id in appsToInstall) {
|
||||
await updateFn(id);
|
||||
downloadResults.add(await downloadFn(id));
|
||||
}
|
||||
} else {
|
||||
List<String> ids = await Future.wait(
|
||||
appsToInstall.map((id) => updateFn(id, skipInstalls: true)));
|
||||
for (var id in ids) {
|
||||
if (!errors.appIdNames.containsKey(id)) {
|
||||
await updateFn(id);
|
||||
}
|
||||
downloadResults = await Future.wait(
|
||||
appsToInstall.map((id) => downloadFn(id, skipInstalls: true)));
|
||||
}
|
||||
for (var res in downloadResults) {
|
||||
if (!errors.appIdNames.containsKey(res['id'])) {
|
||||
await installFn(
|
||||
res['id'] as String,
|
||||
res['willBeSilent'] as bool,
|
||||
res['downloadedFile'] as DownloadedApk?,
|
||||
res['downloadedDir'] as DownloadedXApkDir?);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,17 +1166,6 @@ class AppsProvider with ChangeNotifier {
|
||||
: false;
|
||||
}
|
||||
|
||||
Future<void> updateInstallStatusInMemory(AppInMemory app) async {
|
||||
apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id);
|
||||
apps[app.app.id]?.icon =
|
||||
await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon();
|
||||
apps[app.app.id]?.app.name = await (apps[app.app.id]
|
||||
?.installedInfo
|
||||
?.applicationInfo
|
||||
?.getAppLabel()) ??
|
||||
app.name;
|
||||
}
|
||||
|
||||
Future<void> loadApps({String? singleId}) async {
|
||||
while (loadingApps) {
|
||||
await Future.delayed(const Duration(microseconds: 1));
|
||||
@@ -1163,67 +1174,75 @@ class AppsProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
var sp = SourceProvider();
|
||||
List<List<String>> errors = [];
|
||||
List<App?> newApps = (await getAppsDir()) // Parse Apps from JSON
|
||||
var installedAppsData = await getAllInstalledInfo();
|
||||
List<String> removedAppIds = [];
|
||||
await Future.wait((await getAppsDir()) // Parse Apps from JSON
|
||||
.listSync()
|
||||
.where((item) => item.path.toLowerCase().endsWith('.json'))
|
||||
.where((item) =>
|
||||
singleId == null ||
|
||||
item.path.split('/').last.toLowerCase() ==
|
||||
'${singleId.toLowerCase()}.json')
|
||||
.map((e) {
|
||||
try {
|
||||
return App.fromJson(jsonDecode(File(e.path).readAsStringSync()));
|
||||
} catch (err) {
|
||||
if (err is FormatException) {
|
||||
logs.add('Corrupt JSON when loading App (will be ignored): $e');
|
||||
e.renameSync('${e.path}.corrupt');
|
||||
} else {
|
||||
rethrow;
|
||||
.map((item) async {
|
||||
App? app;
|
||||
if (item.path.toLowerCase().endsWith('.json') &&
|
||||
(singleId == null ||
|
||||
item.path.split('/').last.toLowerCase() ==
|
||||
'${singleId.toLowerCase()}.json')) {
|
||||
try {
|
||||
app = App.fromJson(jsonDecode(File(item.path).readAsStringSync()));
|
||||
} catch (err) {
|
||||
if (err is FormatException) {
|
||||
logs.add('Corrupt JSON when loading App (will be ignored): $e');
|
||||
item.renameSync('${item.path}.corrupt');
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).toList();
|
||||
for (var app in newApps) {
|
||||
// Put Apps into memory to list them (fast)
|
||||
if (app != null) {
|
||||
// Save the app to the in-memory list without grabbing any OS info first
|
||||
apps.update(
|
||||
app.id,
|
||||
(value) => AppInMemory(
|
||||
app!, value.downloadProgress, value.installedInfo, value.icon),
|
||||
ifAbsent: () => AppInMemory(app!, null, null, null));
|
||||
notifyListeners();
|
||||
try {
|
||||
// Try getting the app's source to ensure no invalid apps get loaded
|
||||
sp.getSource(app.url, overrideSource: app.overrideSource);
|
||||
// If the app is installed, grab its OS data and reconcile install statuses
|
||||
PackageInfo? installedInfo;
|
||||
try {
|
||||
installedInfo =
|
||||
installedAppsData.firstWhere((i) => i.packageName == app!.id);
|
||||
} catch (e) {
|
||||
// If the app isn't installed the above throws an error
|
||||
}
|
||||
// Reconcile differences between the installed and recorded install info
|
||||
var moddedApp =
|
||||
getCorrectedInstallStatusAppIfPossible(app, installedInfo);
|
||||
if (moddedApp != null) {
|
||||
app = moddedApp;
|
||||
// Note the app ID if it was uninstalled externally
|
||||
if (moddedApp.installedVersion == null) {
|
||||
removedAppIds.add(moddedApp.id);
|
||||
}
|
||||
}
|
||||
// Update the app in memory with install info and corrections
|
||||
apps.update(
|
||||
app.id,
|
||||
(value) => AppInMemory(
|
||||
app, value.downloadProgress, value.installedInfo, value.icon),
|
||||
ifAbsent: () => AppInMemory(app, null, null, null));
|
||||
app!, value.downloadProgress, installedInfo, value.icon),
|
||||
ifAbsent: () => AppInMemory(app!, null, installedInfo, null));
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
errors.add([app.id, app.finalName, e.toString()]);
|
||||
errors.add([app!.id, app.finalName, e.toString()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}));
|
||||
if (errors.isNotEmpty) {
|
||||
removeApps(errors.map((e) => e[0]).toList());
|
||||
NotificationsProvider().notify(
|
||||
AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList()));
|
||||
}
|
||||
// Get install status and other OS info for each App (slow)
|
||||
await Future.wait(apps.values.map((app) {
|
||||
return updateInstallStatusInMemory(app);
|
||||
}));
|
||||
notifyListeners();
|
||||
// Reconcile version differences
|
||||
List<App> modifiedApps = [];
|
||||
for (var app in apps.values) {
|
||||
var moddedApp =
|
||||
getCorrectedInstallStatusAppIfPossible(app.app, app.installedInfo);
|
||||
if (moddedApp != null) {
|
||||
modifiedApps.add(moddedApp);
|
||||
}
|
||||
}
|
||||
if (modifiedApps.isNotEmpty) {
|
||||
await saveApps(modifiedApps, attemptToCorrectInstallStatus: false);
|
||||
var removedAppIds = modifiedApps
|
||||
.where((a) => a.installedVersion == null)
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
// After reconciliation, delete externally uninstalled Apps if needed
|
||||
// Delete externally uninstalled Apps if needed
|
||||
if (removedAppIds.isNotEmpty) {
|
||||
if (removedAppIds.isNotEmpty) {
|
||||
if (settingsProvider.removeOnExternalUninstall) {
|
||||
await removeApps(removedAppIds);
|
||||
@@ -1234,11 +1253,27 @@ class AppsProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> updateAppIcon(String? appId) async {
|
||||
if (apps[appId]?.icon == null) {
|
||||
var icon =
|
||||
(await apps[appId]?.installedInfo?.applicationInfo?.getAppIcon());
|
||||
if (icon != null) {
|
||||
apps.update(
|
||||
apps[appId]!.app.id,
|
||||
(value) => AppInMemory(apps[appId]!.app, value.downloadProgress,
|
||||
value.installedInfo, icon),
|
||||
ifAbsent: () => AppInMemory(
|
||||
apps[appId]!.app, null, apps[appId]?.installedInfo, icon));
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveApps(List<App> apps,
|
||||
{bool attemptToCorrectInstallStatus = true,
|
||||
bool onlyIfExists = true}) async {
|
||||
attemptToCorrectInstallStatus = attemptToCorrectInstallStatus;
|
||||
for (var a in apps) {
|
||||
await Future.wait(apps.map((a) async {
|
||||
var app = a.deepCopy();
|
||||
PackageInfo? info = await getInstalledInfo(app.id);
|
||||
var icon = await info?.applicationInfo?.getAppIcon();
|
||||
@@ -1260,14 +1295,14 @@ class AppsProvider with ChangeNotifier {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
notifyListeners();
|
||||
export(isAuto: true);
|
||||
}
|
||||
|
||||
Future<void> removeApps(List<String> appIds) async {
|
||||
var apkFiles = APKDir.listSync();
|
||||
for (var appId in appIds) {
|
||||
await Future.wait(appIds.map((appId) async {
|
||||
File file = File('${(await getAppsDir()).path}/$appId.json');
|
||||
if (file.existsSync()) {
|
||||
file.deleteSync(recursive: true);
|
||||
@@ -1281,7 +1316,7 @@ class AppsProvider with ChangeNotifier {
|
||||
if (apps.containsKey(appId)) {
|
||||
apps.remove(appId);
|
||||
}
|
||||
}
|
||||
}));
|
||||
if (appIds.isNotEmpty) {
|
||||
notifyListeners();
|
||||
export(isAuto: true);
|
||||
@@ -1540,6 +1575,8 @@ class AppsProvider with ChangeNotifier {
|
||||
settingsMap.forEach((key, value) {
|
||||
if (value is int) {
|
||||
settingsProvider.prefs?.setInt(key, value);
|
||||
} else if (value is double) {
|
||||
settingsProvider.prefs?.setDouble(key, value);
|
||||
} else if (value is bool) {
|
||||
settingsProvider.prefs?.setBool(key, value);
|
||||
} else if (value is List) {
|
||||
|
@@ -354,11 +354,13 @@ preStandardizeUrl(String url) {
|
||||
url.toLowerCase().indexOf('https://') != 0) {
|
||||
url = 'https://$url';
|
||||
}
|
||||
var trailingSlash = Uri.tryParse(url)?.path.endsWith('/') ?? false;
|
||||
url = url
|
||||
.split('/')
|
||||
.where((e) => e.isNotEmpty)
|
||||
.join('/')
|
||||
.replaceFirst(':/', '://');
|
||||
.split('/')
|
||||
.where((e) => e.isNotEmpty)
|
||||
.join('/')
|
||||
.replaceFirst(':/', '://') +
|
||||
(trailingSlash ? '/' : '');
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -523,8 +525,7 @@ abstract class AppSource {
|
||||
[GeneratedFormTextField('appName', label: tr('appName'), required: false)],
|
||||
[
|
||||
GeneratedFormSwitch('shizukuPretendToBeGooglePlay',
|
||||
label: tr('shizukuPretendToBeGooglePlay'),
|
||||
defaultValue: false)
|
||||
label: tr('shizukuPretendToBeGooglePlay'), defaultValue: false)
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('exemptFromBackgroundUpdates',
|
||||
|
74
pubspec.lock
74
pubspec.lock
@@ -47,18 +47,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "1c2b9e9c56d80d17610bcbd111b37187875c5d0ded8654caa1bda14ea753d001"
|
||||
sha256: "96e677810b83707ff5e10fac11e4839daa0ea4e0123c35864c092699165eb3db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.1"
|
||||
version: "6.1.1"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
|
||||
sha256: "6bd38d335f0954f5fad9c79e614604fbf03a0e5b975923dd001b6ea965ef5b4b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
version: "3.6.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -279,18 +279,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flex_color_picker
|
||||
sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a"
|
||||
sha256: "31b27677d8d8400e4cff5edb3f189f606dd964d608779b6ae1b7ddad37ea48c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
version: "3.5.0"
|
||||
flex_seed_scheme:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flex_seed_scheme
|
||||
sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01"
|
||||
sha256: fb66cdb8ca89084e79efcad2bc2d9deb144666875116f08cdd8d9f8238c8b3ab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
version: "2.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -443,18 +443,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
||||
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.7"
|
||||
version: "4.2.0"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
version: "0.19.0"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -467,26 +467,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
version: "10.0.4"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.3"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -496,7 +496,7 @@ packages:
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
markdown:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: markdown
|
||||
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||
@@ -523,10 +523,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.12.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -857,10 +857,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
version: "0.7.0"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -889,10 +889,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
||||
sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.1"
|
||||
version: "6.3.2"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -961,10 +961,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
version: "14.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -977,18 +977,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932"
|
||||
sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.7.0"
|
||||
version: "4.8.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91
|
||||
sha256: "2282ba2320af34b2bd5320156c664d73f3f022341ed78847bc87723bf88c142f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.16.1"
|
||||
version: "3.16.2"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1001,18 +1001,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
|
||||
sha256: "7affdf9d680c015b11587181171d3cad8093e449db1f7d9f0f08f4f33d24f9a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.13.0"
|
||||
version: "3.13.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
version: "5.5.1"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1046,5 +1046,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.3.3 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
|
@@ -17,7 +17,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.1.8+2265
|
||||
version: 1.1.10+2267
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
@@ -79,6 +79,7 @@ dependencies:
|
||||
url: https://github.com/re7gog/shizuku_apk_installer
|
||||
ref: master
|
||||
|
||||
markdown: any
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
Reference in New Issue
Block a user