Certificate mismatch and other small UX improvements

This commit is contained in:
franzap
2024-10-23 13:08:58 -03:00
parent 3500ccae8b
commit 2d6336bf4f
9 changed files with 38 additions and 25 deletions
+1
View File
@@ -13,6 +13,7 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:enableOnBackInvokedCallback="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
+21 -4
View File
@@ -75,6 +75,17 @@ class App extends BaseApp with DataModelMixin<App> {
final notifier =
adapter.ref.read(installationProgressProvider(id!).notifier);
if (canUpdate) {
final match = await packageCertificateMatches();
if (match == false) {
notifier.state = ErrorInstallProgress(
Exception('APK certificate mismatch'),
info: 'Update is not possible',
);
}
return;
}
final installPermission = await Permission.requestInstallPackages.status;
if (!installPermission.isGranted) {
final newStatus = await Permission.requestInstallPackages.request();
@@ -101,8 +112,8 @@ class App extends BaseApp with DataModelMixin<App> {
notifier.state = ErrorInstallProgress(Exception(e));
return;
}
notifier.state = RequestInstallProgress();
notifier.state = RequestInstallProgress();
final result = await InstallPlugin.install(file.path);
if (result['isSuccess']) {
@@ -110,7 +121,7 @@ class App extends BaseApp with DataModelMixin<App> {
await adapter.ref.localApps.localAppAdapter
.refreshUpdateStatus(appId: identifier);
} else {
const msg = 'Android installation failed';
const msg = 'App has not been installed';
notifier.state = ErrorInstallProgress(
Exception(msg),
info: result['errorMessage'],
@@ -162,13 +173,15 @@ class App extends BaseApp with DataModelMixin<App> {
}
Future<bool?> packageCertificateMatches() async {
if (latestMetadata!.apkSignatureHash == null) return null;
final flags = PackageInfoFlags(
{PMFlag.getPermissions, PMFlag.getSigningCertificates},
);
final i = await packageManager.getPackageInfo(
packageName: id!.toString(), flags: flags);
if (i == null || latestMetadata!.apkSignatureHash == null) {
packageName: identifier, flags: flags);
if (i == null) {
return null;
}
final bytes = i.signingInfo!.signingCertificateHistory!.first;
@@ -425,6 +438,10 @@ class ErrorInstallProgress extends AppInstallProgress {
ErrorInstallProgress(this.e, {this.info});
}
extension ExceptionExt on Exception {
String get message => (this as dynamic).message ?? toString();
}
final installationProgressProvider =
StateProvider.family<AppInstallProgress, Object>(
(_, arg) => IdleInstallProgress());
-12
View File
@@ -231,18 +231,6 @@ class AppDetailScreen extends HookConsumerWidget {
.apkSignatureHash!));
context.showInfo(
'Copied APK certificate SHA-256 to the clipboard');
app
.packageCertificateMatches()
.then((match) {
if (match != null && !match) {
context.showError(
title:
'APK certificate mismatch!',
description:
'Please let us know',
);
}
});
},
child: Row(
mainAxisSize: MainAxisSize.min,
+1 -1
View File
@@ -58,7 +58,7 @@ extension StringWidget on String {
});
}
String substringMin(int size) {
String substringMax(int size) {
return substring(0, min(length, 80));
}
}
+1 -1
View File
@@ -84,7 +84,7 @@ class LoginContainer extends HookConsumerWidget {
}).catchError((e, stack) {
context.showError(
title: e.message ?? e.toString(),
description: stack?.toString().substringMin(200));
description: stack?.toString().substringMax(200));
});
},
builder: (context, child, callback, buttonState) {
-2
View File
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zapstore/main.dart';
import 'package:zapstore/main.data.dart';
import 'package:zapstore/models/app.dart';
import 'package:zapstore/utils/system_info.dart';
import 'package:zapstore/widgets/app_drawer.dart';
+1 -1
View File
@@ -49,7 +49,7 @@ class ErrorContainer extends HookConsumerWidget {
),
Gap(6),
Text(
stack?.toString().substringMin(80) ?? '',
stack?.toString().substringMax(80) ?? '',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
+9 -4
View File
@@ -54,8 +54,7 @@ class InstallButton extends ConsumerWidget {
},
ErrorInstallProgress(:final e, :final info) => () {
// show error and reset state to idle
context.showError(
title: (e as dynamic).message, description: info);
context.showError(title: e.message, description: info);
ref.read(installationProgressProvider(app.id!).notifier).state =
IdleInstallProgress();
},
@@ -109,7 +108,13 @@ class InstallButton extends ConsumerWidget {
VerifyingHashProgress() => Text('Verifying file integrity'),
RequestInstallProgress() =>
Text('Requesting ${app.canUpdate ? 'update' : 'installation'}'),
ErrorInstallProgress() => Text('Error, tap to see message'),
ErrorInstallProgress(:final e) => Padding(
padding: const EdgeInsets.only(left: 8, right: 8),
child: Text(
'${e.message.substringMax(64)} (tap for more)',
textAlign: TextAlign.center,
),
),
}
},
),
@@ -187,7 +192,7 @@ class InstallAlertDialog extends ConsumerWidget {
child: user != null
? Text('Install', style: TextStyle(fontWeight: FontWeight.bold))
: Text(
'I trust the signer, install anyway',
'I trust the signer, install the app',
textAlign: TextAlign.right,
),
),
+4
View File
@@ -29,6 +29,10 @@ class WebOfTrustContainer extends HookConsumerWidget {
trustedUsers.firstWhereOrNull((u) => u.npub == npub) != null;
final trustedUsersWithoutUser =
trustedUsers.where((u) => u.npub != npub).toList();
if (trustedUsersWithoutUser.isEmpty) {
return Text(
'No trusted users. This may be a service error and you may want to try again later.');
}
return RichText(
text: TextSpan(
children: [