about: bug reporting instructions
This commit is contained in:
parent
f16645dd34
commit
cbba70d069
12 changed files with 295 additions and 33 deletions
|
@ -14,4 +14,4 @@ __We collect anonymous data to improve the app.__ We use Google Firebase for Cra
|
|||
## Links
|
||||
[Sources](https://github.com/deckerst/aves)
|
||||
|
||||
[License](https://github.com/deckerst/aves/blob/master/LICENSE)
|
||||
[License](https://github.com/deckerst/aves/blob/main/LICENSE)
|
||||
|
|
|
@ -363,8 +363,11 @@
|
|||
|
||||
"aboutPageTitle": "About",
|
||||
"@aboutPageTitle": {},
|
||||
"aboutFlutter": "Flutter",
|
||||
"@aboutFlutter": {},
|
||||
"aboutLinkSources": "Sources",
|
||||
"@aboutLinkSources": {},
|
||||
"aboutLinkLicense": "License",
|
||||
"@aboutLinkLicense": {},
|
||||
|
||||
"aboutUpdate": "New Version Available",
|
||||
"@aboutUpdate": {},
|
||||
"aboutUpdateLinks1": "A new version of Aves is available on",
|
||||
|
@ -377,12 +380,29 @@
|
|||
"@aboutUpdateGitHub": {},
|
||||
"aboutUpdateGooglePlay": "Google Play",
|
||||
"@aboutUpdateGooglePlay": {},
|
||||
|
||||
"aboutBug": "Bug Report",
|
||||
"@aboutBug": {},
|
||||
"aboutBugSaveLogInstruction": "Save app logs to a file",
|
||||
"@aboutBugSaveLogInstruction": {},
|
||||
"aboutBugSaveLogButton": "Save",
|
||||
"@aboutBugSaveLogButton": {},
|
||||
"aboutBugCopyInfoInstruction": "Copy system information",
|
||||
"@aboutBugCopyInfoInstruction": {},
|
||||
"aboutBugCopyInfoButton": "Copy",
|
||||
"@aboutBugCopyInfoButton": {},
|
||||
"aboutBugReportInstruction": "Report on GitHub with the logs and system information",
|
||||
"@aboutBugReportInstruction": {},
|
||||
"aboutBugReportButton": "Report",
|
||||
"@aboutBugReportButton": {},
|
||||
|
||||
"aboutCredits": "Credits",
|
||||
"@aboutCredits": {},
|
||||
"aboutCreditsWorldAtlas1": "This app uses a TopoJSON file from",
|
||||
"@aboutCreditsWorldAtlas1": {},
|
||||
"aboutCreditsWorldAtlas2": "under ISC License.",
|
||||
"@aboutCreditsWorldAtlas2": {},
|
||||
|
||||
"aboutLicenses": "Open-Source Licenses",
|
||||
"@aboutLicenses": {},
|
||||
"aboutLicensesBanner": "This app uses the following open-source packages and libraries.",
|
||||
|
@ -714,7 +734,7 @@
|
|||
|
||||
"settingsSectionPrivacy": "Privacy",
|
||||
"@settingsSectionPrivacy": {},
|
||||
"settingsEnableCrashReport": "Allow anonymous crash reporting",
|
||||
"settingsEnableCrashReport": "Allow anonymous error reporting",
|
||||
"@settingsEnableCrashReport": {},
|
||||
"settingsSaveSearchHistory": "Save search history",
|
||||
"@settingsSaveSearchHistory": {},
|
||||
|
|
|
@ -170,16 +170,28 @@
|
|||
"menuActionStats": "통계",
|
||||
|
||||
"aboutPageTitle": "앱 정보",
|
||||
"aboutFlutter": "플러터",
|
||||
"aboutLinkSources": "소스 코드",
|
||||
"aboutLinkLicense": "라이선스",
|
||||
|
||||
"aboutUpdate": "업데이트 사용 가능",
|
||||
"aboutUpdateLinks1": "앱의 최신 버전을",
|
||||
"aboutUpdateLinks2": "와",
|
||||
"aboutUpdateLinks3": "에서 다운로드 사용 가능합니다.",
|
||||
"aboutUpdateGitHub": "깃허브",
|
||||
"aboutUpdateGooglePlay": "구글 플레이",
|
||||
|
||||
"aboutBug": "버그 보고",
|
||||
"aboutBugSaveLogInstruction": "앱 로그를 파일에 저장하기",
|
||||
"aboutBugSaveLogButton": "저장",
|
||||
"aboutBugCopyInfoInstruction": "시스템 정보를 복사하기",
|
||||
"aboutBugCopyInfoButton": "복사",
|
||||
"aboutBugReportInstruction": "로그와 시스템 정보를 첨부하여 깃허브에서 이슈를 제툴하기",
|
||||
"aboutBugReportButton": "제출",
|
||||
|
||||
"aboutCredits": "크레딧",
|
||||
"aboutCreditsWorldAtlas1": "이 앱은",
|
||||
"aboutCreditsWorldAtlas2": "의 TopoJSON 파일(ISC 라이선스)을 이용합니다.",
|
||||
|
||||
"aboutLicenses": "오픈 소스 라이선스",
|
||||
"aboutLicensesBanner": "이 앱은 다음의 오픈 소스 패키지와 라이브러리를 이용합니다.",
|
||||
"aboutLicensesAndroidLibraries": "안드로이드 라이브러리",
|
||||
|
|
|
@ -50,6 +50,7 @@ class MimeTypes {
|
|||
static const webm = 'video/webm';
|
||||
|
||||
static const json = 'application/json';
|
||||
static const plainText = 'text/plain';
|
||||
|
||||
// groups
|
||||
|
||||
|
|
|
@ -102,4 +102,7 @@ class AIcons {
|
|||
static const IconData threeSixty = Icons.threesixty_outlined;
|
||||
static const IconData selected = Icons.check_circle_outline;
|
||||
static const IconData unselected = Icons.radio_button_unchecked;
|
||||
|
||||
static const IconData github = MdiIcons.github;
|
||||
static const IconData legal = MdiIcons.scaleBalance;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ class Constants {
|
|||
|
||||
static const int infoGroupMaxValueLength = 140;
|
||||
|
||||
static const String avesGithub = 'https://github.com/deckerst/aves';
|
||||
|
||||
static const List<Dependency> androidDependencies = [
|
||||
Dependency(
|
||||
name: 'AndroidX Core-KTX',
|
||||
|
@ -91,6 +93,12 @@ class Constants {
|
|||
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/connectivity_plus/LICENSE',
|
||||
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Device Info Plus',
|
||||
license: 'BSD 3-Clause',
|
||||
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/device_info_plus/device_info_plus/LICENSE',
|
||||
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus',
|
||||
),
|
||||
Dependency(
|
||||
name: 'FlutterFire (Core, Crashlytics)',
|
||||
license: 'BSD 3-Clause',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:aves/widgets/about/app_ref.dart';
|
||||
import 'package:aves/widgets/about/bug_report.dart';
|
||||
import 'package:aves/widgets/about/credits.dart';
|
||||
import 'package:aves/widgets/about/licenses.dart';
|
||||
import 'package:aves/widgets/about/update.dart';
|
||||
|
@ -27,6 +28,8 @@ class AboutPage extends StatelessWidget {
|
|||
AppReference(),
|
||||
Divider(),
|
||||
AboutUpdate(),
|
||||
BugReport(),
|
||||
Divider(),
|
||||
AboutCredits(),
|
||||
Divider(),
|
||||
],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:aves/flutter_version.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/common/basic/link_chip.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_logo.dart';
|
||||
|
@ -29,8 +30,8 @@ class _AppReferenceState extends State<AppReference> {
|
|||
child: Column(
|
||||
children: [
|
||||
_buildAvesLine(),
|
||||
_buildFlutterLine(),
|
||||
const SizedBox(height: 16),
|
||||
_buildLinks(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -47,37 +48,45 @@ class _AppReferenceState extends State<AppReference> {
|
|||
return FutureBuilder<PackageInfo>(
|
||||
future: _packageInfoLoader,
|
||||
builder: (context, snapshot) {
|
||||
return LinkChip(
|
||||
leading: AvesLogo(
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AvesLogo(
|
||||
size: style.fontSize! * MediaQuery.textScaleFactorOf(context) * 1.25,
|
||||
),
|
||||
text: '${context.l10n.appName} ${snapshot.data?.version}',
|
||||
url: 'https://github.com/deckerst/aves',
|
||||
textStyle: style,
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${context.l10n.appName} ${snapshot.data?.version}',
|
||||
style: style,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFlutterLine() {
|
||||
final style = DefaultTextStyle.of(context).style;
|
||||
final subColor = style.color!.withOpacity(.6);
|
||||
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
Widget _buildLinks() {
|
||||
return Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 4),
|
||||
child: FlutterLogo(
|
||||
size: style.fontSize! * 1.25,
|
||||
LinkChip(
|
||||
leading: const Icon(
|
||||
AIcons.github,
|
||||
size: 24,
|
||||
),
|
||||
text: context.l10n.aboutLinkSources,
|
||||
url: Constants.avesGithub,
|
||||
),
|
||||
LinkChip(
|
||||
leading: const Icon(
|
||||
AIcons.legal,
|
||||
size: 22,
|
||||
),
|
||||
text: context.l10n.aboutLinkLicense,
|
||||
url: '${Constants.avesGithub}/blob/main/LICENSE',
|
||||
),
|
||||
TextSpan(text: '${context.l10n.aboutFlutter} ${version['frameworkVersion']}'),
|
||||
],
|
||||
),
|
||||
style: TextStyle(color: subColor),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
163
lib/widgets/about/bug_report.dart
Normal file
163
lib/widgets/about/bug_report.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves/flutter_version.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/services.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class BugReport extends StatefulWidget {
|
||||
const BugReport({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_BugReportState createState() => _BugReportState();
|
||||
}
|
||||
|
||||
class _BugReportState extends State<BugReport> with FeedbackMixin {
|
||||
late Future<String> _infoLoader;
|
||||
bool _showInstructions = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_infoLoader = _getInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return ExpansionPanelList(
|
||||
expansionCallback: (index, isExpanded) {
|
||||
setState(() => _showInstructions = !isExpanded);
|
||||
},
|
||||
expandedHeaderPadding: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
children: [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (context, isExpanded) => ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 48),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Text(l10n.aboutBug, style: Constants.titleTextStyle),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildStep(1, l10n.aboutBugSaveLogInstruction, l10n.aboutBugSaveLogButton, _saveLogs),
|
||||
_buildStep(2, l10n.aboutBugCopyInfoInstruction, l10n.aboutBugCopyInfoButton, _copySystemInfo),
|
||||
FutureBuilder<String>(
|
||||
future: _infoLoader,
|
||||
builder: (context, snapshot) {
|
||||
final info = snapshot.data;
|
||||
if (info == null) return const SizedBox();
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade800,
|
||||
border: Border.all(
|
||||
color: Colors.white,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: SelectableText(info));
|
||||
},
|
||||
),
|
||||
_buildStep(3, l10n.aboutBugReportInstruction, l10n.aboutBugReportButton, _goToGithub),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
isExpanded: _showInstructions,
|
||||
canTapOnHeader: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep(int step, String text, String buttonText, VoidCallback onPressed) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(BorderSide(
|
||||
color: Theme.of(context).accentColor,
|
||||
width: AvesFilterChip.outlineWidth,
|
||||
)),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text('$step'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(text)),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
style: ButtonStyle(
|
||||
side: MaterialStateProperty.all<BorderSide>(BorderSide(color: Theme.of(context).accentColor)),
|
||||
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
|
||||
),
|
||||
child: Text(buttonText),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _getInfo() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||
final hasPlayServices = await availability.hasPlayServices;
|
||||
return [
|
||||
'Aves version: ${packageInfo.version} (Build ${packageInfo.buildNumber})',
|
||||
'Flutter version: ${version['frameworkVersion']} (Channel ${version['channel']})',
|
||||
'Android version: ${androidInfo.version.release} (SDK ${androidInfo.version.sdkInt})',
|
||||
'Device: ${androidInfo.manufacturer} ${androidInfo.model}',
|
||||
'Google Play services: ${hasPlayServices ? 'ready' : 'not available'}',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
Future<void> _saveLogs() async {
|
||||
final result = await Process.run('logcat', ['-d']);
|
||||
final logs = result.stdout;
|
||||
final success = await storageService.createFile(
|
||||
'aves-logs-${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}.txt',
|
||||
MimeTypes.plainText,
|
||||
Uint8List.fromList(utf8.encode(logs)),
|
||||
);
|
||||
if (success != null) {
|
||||
if (success) {
|
||||
showFeedback(context, context.l10n.genericSuccessFeedback);
|
||||
} else {
|
||||
showFeedback(context, context.l10n.genericFailureFeedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _copySystemInfo() async {
|
||||
await Clipboard.setData(ClipboardData(text: await _infoLoader));
|
||||
showFeedback(context, context.l10n.genericSuccessFeedback);
|
||||
}
|
||||
|
||||
Future<void> _goToGithub() async {
|
||||
await launch('${Constants.avesGithub}/issues/new');
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ class _AboutUpdateState extends State<AboutUpdate> {
|
|||
WidgetSpan(
|
||||
child: LinkChip(
|
||||
text: context.l10n.aboutUpdateGitHub,
|
||||
url: 'https://github.com/deckerst/aves/releases',
|
||||
url: '${Constants.avesGithub}/releases',
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -190,6 +190,48 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
device_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
device_info_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
device_info_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
device_info_plus_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
device_info_plus_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -19,6 +19,7 @@ dependencies:
|
|||
# TODO TLAD as of 2021/08/04, null safe version is pre-release
|
||||
custom_rounded_rectangle_border: '>=0.2.0-nullsafety.0'
|
||||
decorated_icon:
|
||||
device_info_plus:
|
||||
equatable:
|
||||
event_bus:
|
||||
expansion_tile_card:
|
||||
|
|
Loading…
Reference in a new issue