mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 20:48:24 +00:00
- Ability to disable updates, show disabled updates app in updates screen, closes #157
- Better installation feedback messages (new plugin) - Bigger toasts and improved explanations, add toast actions, closes #159 - Add install success toast, closes #154 - Remove default Signed by zap.store - refreshUpdateStatus performance optimization for one app
This commit is contained in:
+57
-19
@@ -1,15 +1,15 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:android_package_installer/android_package_installer.dart';
|
||||
import 'package:android_package_manager/android_package_manager.dart';
|
||||
import 'package:async/async.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:install_plugin/install_plugin.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:purplebase/purplebase.dart';
|
||||
@@ -21,6 +21,7 @@ import 'package:zapstore/models/release.dart';
|
||||
import 'package:zapstore/models/user.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:zapstore/navigation/app_initializer.dart';
|
||||
import 'package:zapstore/utils/extensions.dart';
|
||||
import 'package:zapstore/utils/system_info.dart';
|
||||
|
||||
@@ -66,9 +67,10 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
signer.isPresent;
|
||||
bool get isUpdated => localApp.value?.status == AppInstallStatus.updated;
|
||||
bool get isDowngrade => localApp.value?.status == AppInstallStatus.downgrade;
|
||||
bool get isDisabled => localApp.value?.disabled == true;
|
||||
|
||||
Future<void> install() async {
|
||||
if (!canInstall && !canUpdate) {
|
||||
if (!canInstall && !canUpdate || isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,9 +82,20 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
final match = await packageCertificateMatches();
|
||||
if (match == false) {
|
||||
notifier.state = ErrorInstallProgress(
|
||||
Exception('Update is not possible'),
|
||||
Exception('App failed security check'),
|
||||
info:
|
||||
'App failed a security check (APK cert mismatch). To fix, remove the app.',
|
||||
'''There was an APK certificate mismatch for $name ($identifier).
|
||||
|
||||
This could mean a malicious developer signed this update.
|
||||
|
||||
If you want to update anyway, you need to manually uninstall the app and install it again.
|
||||
|
||||
It could also mean the original developer signed with a different key. If you are sure you installed $name through another app store, you can choose to turn off zap.store updates.
|
||||
''',
|
||||
actions: [
|
||||
('Close notice (recommended)', () async {}),
|
||||
('Disable $name updates', disableUpdates)
|
||||
],
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -116,18 +129,26 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
}
|
||||
|
||||
notifier.state = RequestInstallProgress();
|
||||
final result = await InstallPlugin.install(file.path);
|
||||
|
||||
if (result['isSuccess']) {
|
||||
await file.delete();
|
||||
await adapter.ref.localApps.localAppAdapter
|
||||
.refreshUpdateStatus(appId: identifier);
|
||||
} else {
|
||||
const msg = 'App not installed';
|
||||
notifier.state = ErrorInstallProgress(
|
||||
Exception(msg),
|
||||
info: result['errorMessage'],
|
||||
);
|
||||
final result =
|
||||
await AndroidPackageInstaller.installApk(apkFilePath: file.path);
|
||||
final installationStatus =
|
||||
result != null ? PackageInstallerStatus.byCode(result) : null;
|
||||
switch (installationStatus) {
|
||||
case PackageInstallerStatus.success:
|
||||
notifier.state = IdleInstallProgress(success: true);
|
||||
await file.delete();
|
||||
await adapter.ref.localApps.localAppAdapter
|
||||
.refreshUpdateStatus(appId: identifier);
|
||||
break;
|
||||
case PackageInstallerStatus.failureAborted:
|
||||
// Simply reset state
|
||||
notifier.state = IdleInstallProgress();
|
||||
break;
|
||||
default:
|
||||
notifier.state = ErrorInstallProgress(
|
||||
Exception('Error installing app'),
|
||||
info: installationStatus?.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,8 +220,21 @@ class App extends BaseApp with DataModelMixin<App> {
|
||||
return metadataSigHashes
|
||||
.any((msh) => msh.toLowerCase() == installedApkSigHash);
|
||||
}
|
||||
|
||||
/// Disables updates to this app by putting the app identifier in local storage (shared prefs)
|
||||
Future<void> disableUpdates() async {
|
||||
final adapter = DataModel.adapterFor(localApp.value!) as LocalAppAdapter;
|
||||
final value = sharedPreferences!.getStringList(kDisabledUpdatesKey) ?? [];
|
||||
if (!value.contains(identifier)) {
|
||||
await sharedPreferences!
|
||||
.setStringList(kDisabledUpdatesKey, [...value, identifier!]);
|
||||
}
|
||||
await adapter.refreshUpdateStatus(appId: identifier!);
|
||||
}
|
||||
}
|
||||
|
||||
const kDisabledUpdatesKey = 'disabled_updates';
|
||||
|
||||
mixin AppAdapter on Adapter<App> {
|
||||
final queriedAtMap = <String, DateTime>{};
|
||||
|
||||
@@ -430,7 +464,10 @@ extension AppExt on Iterable<App> {
|
||||
|
||||
sealed class AppInstallProgress {}
|
||||
|
||||
class IdleInstallProgress extends AppInstallProgress {}
|
||||
class IdleInstallProgress extends AppInstallProgress {
|
||||
final bool? success;
|
||||
IdleInstallProgress({this.success});
|
||||
}
|
||||
|
||||
class DownloadingInstallProgress extends AppInstallProgress {
|
||||
final double progress;
|
||||
@@ -444,7 +481,8 @@ class RequestInstallProgress extends AppInstallProgress {}
|
||||
class ErrorInstallProgress extends AppInstallProgress {
|
||||
final Exception e;
|
||||
final String? info;
|
||||
ErrorInstallProgress(this.e, {this.info});
|
||||
final List<(String, Future<void> Function())> actions;
|
||||
ErrorInstallProgress(this.e, {this.info, this.actions = const []});
|
||||
}
|
||||
|
||||
extension ExceptionExt on Exception {
|
||||
|
||||
+66
-23
@@ -6,6 +6,7 @@ import 'package:flutter_data/flutter_data.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:zapstore/main.data.dart';
|
||||
import 'package:zapstore/models/app.dart';
|
||||
import 'package:zapstore/navigation/app_initializer.dart';
|
||||
import 'package:zapstore/utils/system_info.dart';
|
||||
|
||||
part 'local_app.g.dart';
|
||||
@@ -18,23 +19,26 @@ class LocalApp extends DataModel<LocalApp> {
|
||||
final String? installedVersion;
|
||||
final int? installedVersionCode;
|
||||
final AppInstallStatus? status;
|
||||
// final String? apkSignatureHash;
|
||||
final bool disabled;
|
||||
|
||||
LocalApp(
|
||||
{required this.id,
|
||||
this.installedVersion,
|
||||
this.installedVersionCode,
|
||||
this.status});
|
||||
this.status,
|
||||
this.disabled = false});
|
||||
|
||||
LocalApp copyWith(
|
||||
{String? installedVersion,
|
||||
int? installedVersionCode,
|
||||
AppInstallStatus? status}) {
|
||||
AppInstallStatus? status,
|
||||
bool? disabled}) {
|
||||
return LocalApp(
|
||||
id: id,
|
||||
installedVersion: installedVersion ?? this.installedVersion,
|
||||
installedVersionCode: installedVersionCode ?? this.installedVersionCode,
|
||||
status: status ?? this.status,
|
||||
disabled: disabled ?? this.disabled,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -51,35 +55,74 @@ mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
final installedPackageInfos = infos!.where((i) =>
|
||||
!kExcludedAppIdNamespaces.any((e) => i.packageName!.startsWith(e)));
|
||||
|
||||
final ids = appId != null
|
||||
? [appId]
|
||||
: installedPackageInfos.map((i) => i.packageName).nonNulls;
|
||||
final ids = installedPackageInfos.map((i) => i.packageName).nonNulls;
|
||||
final disabledUpdatesIdentifiers =
|
||||
sharedPreferences!.getStringList(kDisabledUpdatesKey) ?? [];
|
||||
|
||||
final localApps = findManyLocalByIds(ids);
|
||||
final apps = ref.apps.appAdapter.findWhereIdentifierInLocal(ids);
|
||||
// appId == null is the case of all apps
|
||||
if (appId == null) {
|
||||
final localApps = findAllLocal();
|
||||
final apps = ref.apps.appAdapter.findWhereIdentifierInLocal(ids);
|
||||
|
||||
for (final i in installedPackageInfos) {
|
||||
final appId = i.packageName!;
|
||||
final app = apps.firstWhereOrNull((a) => a.identifier == appId);
|
||||
final localAppsToSave = <LocalApp>[];
|
||||
|
||||
final installedVersion = i.versionName;
|
||||
final installedVersionCode = i.versionCode;
|
||||
for (final i in installedPackageInfos) {
|
||||
final appId = i.packageName!;
|
||||
final app = apps.firstWhereOrNull((a) => a.identifier == appId);
|
||||
|
||||
final installedVersion = i.versionName;
|
||||
final installedVersionCode = i.versionCode;
|
||||
|
||||
final status =
|
||||
determineUpdateStatus(app, installedVersion, installedVersionCode);
|
||||
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
final localApp = localApps.firstWhereOrNull((app) => appId == app.id) ??
|
||||
LocalApp(id: i.packageName!);
|
||||
localAppsToSave.add(
|
||||
localApp.copyWith(
|
||||
installedVersion: installedVersion,
|
||||
installedVersionCode: installedVersionCode,
|
||||
status: status,
|
||||
disabled: disabledUpdatesIdentifiers.contains(localApp.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Save local apps
|
||||
await saveManyLocal(localAppsToSave, async: true);
|
||||
|
||||
// Remove uninstalled apps
|
||||
final uninstalledApps = localApps.where((a) => !ids.contains(a.id));
|
||||
if (uninstalledApps.isNotEmpty) {
|
||||
deleteLocalByKeys(uninstalledApps.map((a) => DataModel.keyFor(a)));
|
||||
}
|
||||
} else {
|
||||
final app =
|
||||
ref.apps.appAdapter.findWhereIdentifierInLocal({appId}).firstOrNull;
|
||||
final i =
|
||||
installedPackageInfos.firstWhereOrNull((i) => i.packageName == appId);
|
||||
|
||||
final installedVersion = i?.versionName;
|
||||
final installedVersionCode = i?.versionCode;
|
||||
|
||||
final status =
|
||||
determineUpdateStatus(app, installedVersion, installedVersionCode);
|
||||
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
if (status != null) {
|
||||
final localApp = app!.localApp.value ?? LocalApp(id: i!.packageName!);
|
||||
|
||||
final localApp = localApps.firstWhereOrNull((app) => appId == app.id) ??
|
||||
LocalApp(id: i.packageName!);
|
||||
localApp
|
||||
.copyWith(
|
||||
localApp
|
||||
.copyWith(
|
||||
installedVersion: installedVersion,
|
||||
installedVersionCode: installedVersionCode,
|
||||
status: status)
|
||||
.saveLocal();
|
||||
status: status,
|
||||
disabled: disabledUpdatesIdentifiers.contains(localApp.id),
|
||||
)
|
||||
.saveLocal();
|
||||
}
|
||||
}
|
||||
|
||||
updateNumberOfApps();
|
||||
@@ -87,7 +130,7 @@ mixin LocalAppAdapter on Adapter<LocalApp> {
|
||||
|
||||
void updateNumberOfApps() {
|
||||
final rs = db.select(
|
||||
'SELECT count(*) as c FROM localApps WHERE json_extract(data, \'\$.status\') is \'updatable\'');
|
||||
'SELECT count(*) as c FROM localApps WHERE json_extract(data, \'\$.status\') is \'updatable\' AND json_extract(data, \'\$.disabled\') = false');
|
||||
ref.read(appsToUpdateProvider.notifier).state = rs.first['c'];
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ LocalApp _$LocalAppFromJson(Map<String, dynamic> json) => LocalApp(
|
||||
installedVersion: json['installedVersion'] as String?,
|
||||
installedVersionCode: (json['installedVersionCode'] as num?)?.toInt(),
|
||||
status: $enumDecodeNullable(_$AppInstallStatusEnumMap, json['status']),
|
||||
disabled: json['disabled'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LocalAppToJson(LocalApp instance) => <String, dynamic>{
|
||||
@@ -58,6 +59,7 @@ Map<String, dynamic> _$LocalAppToJson(LocalApp instance) => <String, dynamic>{
|
||||
'installedVersion': instance.installedVersion,
|
||||
'installedVersionCode': instance.installedVersionCode,
|
||||
'status': _$AppInstallStatusEnumMap[instance.status],
|
||||
'disabled': instance.disabled,
|
||||
};
|
||||
|
||||
const _$AppInstallStatusEnumMap = {
|
||||
|
||||
@@ -10,17 +10,18 @@ import 'package:zapstore/navigation/router.dart';
|
||||
import 'package:zapstore/widgets/app_curation_container.dart';
|
||||
|
||||
AppLifecycleListener? _lifecycleListener;
|
||||
SharedPreferences? sharedPreferences;
|
||||
|
||||
final appInitializer = FutureProvider<void>((ref) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final dbVersion = prefs.getInt('dbVersion');
|
||||
sharedPreferences = await SharedPreferences.getInstance();
|
||||
final dbVersion = sharedPreferences!.getInt('dbVersion');
|
||||
|
||||
// Perform migration
|
||||
if (dbVersion == null || dbVersion < kDbVersion) {
|
||||
final storage = ref.read(localStorageProvider);
|
||||
await storage.initialize();
|
||||
await storage.destroy();
|
||||
await prefs.setInt('dbVersion', kDbVersion);
|
||||
await sharedPreferences!.setInt('dbVersion', kDbVersion);
|
||||
}
|
||||
|
||||
// Initialize Flutter Data
|
||||
|
||||
@@ -14,7 +14,8 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(updatesProvider);
|
||||
|
||||
final (updatedApps, updatableApps) = state.value ?? ([], []);
|
||||
final (updatedApps, updatableApps, updatesDisabledApps) =
|
||||
state.value ?? ([], [], []);
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
@@ -37,7 +38,9 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
children: [
|
||||
Gap(20),
|
||||
Text(
|
||||
'${updatableApps.length} update${updatableApps.length > 1 ? 's' : ''} available'
|
||||
(updatableApps.length > 1
|
||||
? '${updatableApps.length} updates available'
|
||||
: 'One update available')
|
||||
.toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
@@ -45,7 +48,6 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
fontWeight: FontWeight.w300,
|
||||
),
|
||||
),
|
||||
//
|
||||
Gap(10),
|
||||
for (final app in updatableApps)
|
||||
AppCard(model: app, showUpdate: true),
|
||||
@@ -63,6 +65,19 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
),
|
||||
Gap(10),
|
||||
for (final app in updatedApps) AppCard(model: app),
|
||||
Gap(10),
|
||||
if (updatesDisabledApps.isNotEmpty)
|
||||
Text(
|
||||
'Disabled updates apps'.toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
letterSpacing: 3,
|
||||
fontWeight: FontWeight.w300,
|
||||
),
|
||||
),
|
||||
Gap(10),
|
||||
for (final app in updatesDisabledApps)
|
||||
AppCard(model: app, showUpdate: true),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -71,9 +86,9 @@ class UpdatesScreen extends HookConsumerWidget {
|
||||
|
||||
// Provider
|
||||
|
||||
class UpdatesNotifier extends AsyncNotifier<(List<App>, List<App>)> {
|
||||
class UpdatesNotifier extends AsyncNotifier<(List<App>, List<App>, List<App>)> {
|
||||
@override
|
||||
Future<(List<App>, List<App>)> build() async {
|
||||
Future<(List<App>, List<App>, List<App>)> build() async {
|
||||
// This provider is quite inefficient, as it watches everything
|
||||
// and always triggers as there's no proper equality for the return type
|
||||
// There is also no good way to manage state since returning a value
|
||||
@@ -83,11 +98,11 @@ class UpdatesNotifier extends AsyncNotifier<(List<App>, List<App>)> {
|
||||
final localAppsState = ref.localApps.watchAll();
|
||||
|
||||
if (!appsState.hasModel || !localAppsState.hasModel) {
|
||||
return (<App>[], <App>[]);
|
||||
return (<App>[], <App>[], <App>[]);
|
||||
}
|
||||
|
||||
final updatableApps = appsState.model
|
||||
.where((app) => app.canUpdate)
|
||||
.where((app) => app.canUpdate && !app.isDisabled)
|
||||
.toList()
|
||||
.sorted((a, b) => (a.name?.toLowerCase() ?? '')
|
||||
.compareTo(b.name?.toLowerCase() ?? ''));
|
||||
@@ -98,10 +113,16 @@ class UpdatesNotifier extends AsyncNotifier<(List<App>, List<App>)> {
|
||||
.sorted((a, b) => (a.name?.toLowerCase() ?? '')
|
||||
.compareTo(b.name?.toLowerCase() ?? ''));
|
||||
|
||||
return (updatedApps, updatableApps);
|
||||
final updateDisabledApps = appsState.model
|
||||
.where((app) => app.canUpdate && app.isDisabled)
|
||||
.toList()
|
||||
.sorted((a, b) => (a.name?.toLowerCase() ?? '')
|
||||
.compareTo(b.name?.toLowerCase() ?? ''));
|
||||
|
||||
return (updatedApps, updatableApps, updateDisabledApps);
|
||||
}
|
||||
}
|
||||
|
||||
final updatesProvider =
|
||||
AsyncNotifierProvider<UpdatesNotifier, (List<App>, List<App>)>(
|
||||
AsyncNotifierProvider<UpdatesNotifier, (List<App>, List<App>, List<App>)>(
|
||||
UpdatesNotifier.new);
|
||||
|
||||
+35
-10
@@ -9,37 +9,61 @@ import 'package:zapstore/models/app.dart';
|
||||
|
||||
extension ContextX on BuildContext {
|
||||
ThemeData get theme => Theme.of(this);
|
||||
void showInfo(String message, {Icon? icon}) {
|
||||
void showInfo(String message, {String? description, Icon? icon}) {
|
||||
toastification.show(
|
||||
context: this,
|
||||
type: ToastificationType.info,
|
||||
icon: icon ?? Icon(Icons.info),
|
||||
style: ToastificationStyle.fillColored,
|
||||
alignment: Alignment.topCenter,
|
||||
title: Text(
|
||||
message,
|
||||
style: TextStyle(fontSize: 16),
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
autoCloseDuration: const Duration(seconds: 3),
|
||||
description: description != null ? Text(description) : null,
|
||||
autoCloseDuration: const Duration(seconds: 4),
|
||||
showProgressBar: false,
|
||||
closeOnClick: true,
|
||||
alignment: Alignment.bottomCenter,
|
||||
);
|
||||
}
|
||||
|
||||
void showError({required String title, String? description, Icon? icon}) {
|
||||
void showError(
|
||||
{required String title,
|
||||
String? description,
|
||||
Icon? icon,
|
||||
List<(String, Future<void> Function())> actions = const []}) {
|
||||
toastification.show(
|
||||
context: this,
|
||||
type: ToastificationType.error,
|
||||
style: ToastificationStyle.fillColored,
|
||||
alignment: Alignment.topCenter,
|
||||
icon: icon ?? Icon(Icons.error),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
showProgressBar: false,
|
||||
closeOnClick: true,
|
||||
description: description != null ? Text(description) : null,
|
||||
alignment: Alignment.lerp(Alignment.bottomCenter, Alignment.center, 0.25),
|
||||
description: description != null
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
RichText(text: TextSpan(text: description)),
|
||||
for (final (text, fn) in actions)
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.transparent),
|
||||
onPressed: () async {
|
||||
await fn.call();
|
||||
toastification.dismissAll(delayForAnimation: false);
|
||||
},
|
||||
child: Text(text),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,11 +99,12 @@ HasMany<T> hasMany<T extends DataModelMixin<T>>(Map<String, dynamic> map) {
|
||||
return HasMany<T>.fromJson(map);
|
||||
}
|
||||
|
||||
final emojiParser = EmojiParser();
|
||||
|
||||
extension StringWidget on String {
|
||||
static final _emojiParser = EmojiParser();
|
||||
String parseEmojis() {
|
||||
return replaceAllMapped(RegExp(':([a-z]*):'), (m) {
|
||||
return _emojiParser.hasName(m[1]!) ? _emojiParser.get(m[1]!).code : m[0]!;
|
||||
return emojiParser.hasName(m[1]!) ? emojiParser.get(m[1]!).code : m[0]!;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ class AppCard extends HookConsumerWidget {
|
||||
),
|
||||
Gap(6),
|
||||
if (app.signer.isPresent &&
|
||||
app.signer.value!.pubkey != kZapstorePubkey)
|
||||
(app.signer.value!.pubkey != kZapstorePubkey ||
|
||||
{'store.zap.app'}.contains(app.identifier)))
|
||||
AuthorContainer(
|
||||
user: app.signer.value!,
|
||||
text: 'Signed by',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
@@ -15,7 +16,7 @@ import 'package:zapstore/widgets/author_container.dart';
|
||||
import 'package:zapstore/widgets/spinning_logo.dart';
|
||||
import 'package:zapstore/widgets/wot_container.dart';
|
||||
|
||||
class InstallButton extends ConsumerWidget {
|
||||
class InstallButton extends HookConsumerWidget {
|
||||
InstallButton({
|
||||
super.key,
|
||||
required this.app,
|
||||
@@ -30,6 +31,16 @@ class InstallButton extends ConsumerWidget {
|
||||
final progress = ref.watch(installationProgressProvider(app.id!));
|
||||
final status = app.localApp.value?.status;
|
||||
|
||||
useMemoized(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (progress is IdleInstallProgress && progress.success == true) {
|
||||
context.showInfo('Success',
|
||||
description:
|
||||
'${app.name ?? app.identifier} was successfully installed');
|
||||
}
|
||||
});
|
||||
}, [app.id, progress]);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: switch (status) {
|
||||
AppInstallStatus.downgrade => null,
|
||||
@@ -47,15 +58,18 @@ class InstallButton extends ConsumerWidget {
|
||||
);
|
||||
} else if (app.canUpdate) {
|
||||
app.install();
|
||||
} else if (app.isDisabled) {
|
||||
// nothing
|
||||
} else {
|
||||
context.showError(
|
||||
title: 'Can\'t install',
|
||||
title: 'Installation not possible',
|
||||
description: 'Release or signer are missing.');
|
||||
}
|
||||
},
|
||||
ErrorInstallProgress(:final e, :final info) => () {
|
||||
ErrorInstallProgress(:final e, :final info, :final actions) => () {
|
||||
// show error and reset state to idle
|
||||
context.showError(title: e.message, description: info);
|
||||
context.showError(
|
||||
title: e.message, description: info, actions: actions);
|
||||
ref.read(installationProgressProvider(app.id!).notifier).state =
|
||||
IdleInstallProgress();
|
||||
},
|
||||
@@ -74,6 +88,7 @@ class InstallButton extends ConsumerWidget {
|
||||
},
|
||||
backgroundColor: switch (progress) {
|
||||
ErrorInstallProgress() => Colors.red,
|
||||
IdleInstallProgress() => app.isDisabled ? Colors.grey : kUpdateColor,
|
||||
_ => kUpdateColor,
|
||||
},
|
||||
progressColor: Colors.blue[800],
|
||||
@@ -91,7 +106,7 @@ class InstallButton extends ConsumerWidget {
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: AutoSizeText(
|
||||
'Update',
|
||||
app.isDisabled ? 'Disabled updates' : 'Update',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -109,10 +124,10 @@ class InstallButton extends ConsumerWidget {
|
||||
VerifyingHashProgress() => Text('Verifying file integrity'),
|
||||
RequestInstallProgress() =>
|
||||
Text('Requesting ${app.canUpdate ? 'update' : 'installation'}'),
|
||||
ErrorInstallProgress(:final e) => Padding(
|
||||
ErrorInstallProgress() => Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: Text(
|
||||
'${e.message.substringMax(64)} (tap for more)',
|
||||
'Error (tap for details)',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -9,7 +9,7 @@ import app_links
|
||||
import device_info_plus
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import sqflite
|
||||
import sqflite_darwin
|
||||
import sqlite3_flutter_libs
|
||||
import url_launcher_macos
|
||||
|
||||
|
||||
+118
-80
@@ -5,18 +5,32 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "67.0.0"
|
||||
version: "72.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
version: "6.7.0"
|
||||
android_package_installer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: ba2aa7a
|
||||
resolved-ref: ba2aa7a11edc2649d1d80c25ed9291521262f714
|
||||
url: "https://github.com/zapstore/android_package_installer"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
android_package_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -69,10 +83,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
async:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -165,18 +179,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7"
|
||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.11"
|
||||
version: "2.4.13"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe
|
||||
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.1"
|
||||
version: "7.3.2"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -237,10 +251,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
|
||||
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1"
|
||||
version: "0.4.2"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -253,10 +267,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.0"
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -269,18 +283,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.2"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.6"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -301,10 +315,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "2.3.7"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -365,18 +379,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -435,10 +449,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: e17575ca576a34b46c58c91f9948891117a1bd97815d2e661813c7f90c647a78
|
||||
sha256: f0e599ba89c9946c8e051780f0ec99aba4ba15895e0380a7ab68f420046fc44e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3+2"
|
||||
version: "0.7.4+1"
|
||||
flutter_phoenix:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -451,10 +465,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: "711d916456563f715bde1e139d7cfdca009f8264befab3ac9f8ded8b6ec26405"
|
||||
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.6.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -509,10 +523,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: "6f1b756f6e863259a99135ff3c95026c3cdca17d10ebef2bba2261a25ddc8bbc"
|
||||
sha256: ce89c5a993ca5eea74535f798478502c30a625ecb10a1de4d7fef5cd1bcac2a4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.0"
|
||||
version: "14.4.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -533,10 +547,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hooks_riverpod
|
||||
sha256: "534ca8af331235f700f3916a74e4579cdfdd708e2fbed342fc8aa969d41a7af0"
|
||||
sha256: "70bba33cfc5670c84b796e6929c54b8bc5be7d0fe15bb28c2560500b9ad06966"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.6.1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -581,10 +595,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "4.3.0"
|
||||
inflection3:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -593,14 +607,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.3+2"
|
||||
install_plugin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: install_plugin
|
||||
sha256: "6fb67ba0781e75de4f2f2266ed25e835bfd277c5bfc2ed034af52774355857c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -677,10 +683,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
markdown:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -717,10 +731,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
version: "2.0.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -749,18 +763,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
|
||||
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.10"
|
||||
version: "2.2.12"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -821,10 +835,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa"
|
||||
sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.12"
|
||||
version: "12.0.13"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -869,10 +883,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -948,10 +962,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: c86fedfb45dd1da98ee6493dd9374325cdf494e7d523ebfb0c387eecc5f7b5c9
|
||||
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.6.1"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -972,10 +986,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.3"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1081,34 +1095,58 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
|
||||
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3+1"
|
||||
version: "2.4.1"
|
||||
sqflite_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_android
|
||||
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||
sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.4+5"
|
||||
sqflite_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_darwin
|
||||
sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sqflite_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_platform_interface
|
||||
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "45f168ae2213201b54e09429ed0c593dc2c88c924a1488d6f9c523a255d567cb"
|
||||
sha256: bb174b3ec2527f9c5f680f73a89af8149dd99782fbb56ea88ad0807c5638f2ed
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.6"
|
||||
version: "2.4.7"
|
||||
sqlite3_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqlite3_flutter_libs
|
||||
sha256: "62bbb4073edbcdf53f40c80775f33eea01d301b7b81417e5b3fb7395416258c1"
|
||||
sha256: "7ae52b23366e5295005022e62fa093f64bfe190810223ea0ebf733a4cd140bce"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.24"
|
||||
version: "0.5.26"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1153,10 +1191,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.3.0+3"
|
||||
system_info2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1201,26 +1239,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.4.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
|
||||
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
version: "6.3.1"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
|
||||
sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.9"
|
||||
version: "6.3.14"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1265,10 +1303,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185"
|
||||
sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.1.3"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1337,10 +1375,10 @@ packages:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: win32
|
||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||
sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.4"
|
||||
version: "5.8.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1374,5 +1412,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
+4
-1
@@ -47,7 +47,10 @@ dependencies:
|
||||
app_links: ^6.3.2
|
||||
dart_emoji: ^0.2.0+2
|
||||
remove_markdown: ^0.0.2
|
||||
install_plugin: ^2.1.0
|
||||
android_package_installer:
|
||||
git:
|
||||
url: https://github.com/zapstore/android_package_installer
|
||||
ref: ba2aa7a
|
||||
shared_preferences: ^2.3.2
|
||||
|
||||
dependency_overrides:
|
||||
|
||||
Reference in New Issue
Block a user