mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 07:48:40 +00:00
add barcode scanner and remove tutorial
This commit is contained in:
parent
9667ea21b6
commit
76b617e63a
13 changed files with 73 additions and 277 deletions
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
|
|
@ -1,9 +1,15 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application android:usesCleartextTraffic="true" >
|
||||
</application>
|
||||
<application android:usesCleartextTraffic="true" >
|
||||
<!-- // https://github.com/juliansteenbakker/mobile_scanner/issues/553 -->
|
||||
<meta-data android:name="firebase_performance_collection_deactivated" android:value="true" />
|
||||
<!-- <service
|
||||
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
|
||||
tools:node="remove">
|
||||
</service> -->
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
<application
|
||||
android:label="twonly"
|
||||
android:name="${applicationName}"
|
||||
|
|
@ -33,6 +33,12 @@
|
|||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
|
||||
<!-- // https://github.com/juliansteenbakker/mobile_scanner/issues/553 -->
|
||||
<meta-data android:name="firebase_performance_collection_deactivated" android:value="true" />
|
||||
<service
|
||||
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
|
||||
tools:node="remove">
|
||||
</service>
|
||||
|
||||
<!-- <service
|
||||
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@
|
|||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<application android:usesCleartextTraffic="true" >
|
||||
</application>
|
||||
<meta-data android:name="firebase_performance_collection_deactivated" android:value="true" />
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -85,5 +85,9 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<!--Disable Firebase Telemetry-->
|
||||
<key>firebase_performance_collection_deactivated</key>
|
||||
<true/>
|
||||
<!--...-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -23,24 +23,26 @@ class BarcodeDetectorPainter extends CustomPainter {
|
|||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint()
|
||||
final paint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 3.0
|
||||
..color = Colors.lightGreenAccent;
|
||||
|
||||
final Paint background = Paint()..color = Color(0x99000000);
|
||||
final background = Paint()..color = const Color(0x99000000);
|
||||
|
||||
for (final Barcode barcode in barcodes) {
|
||||
final ParagraphBuilder builder = ParagraphBuilder(
|
||||
for (final barcode in barcodes) {
|
||||
final builder = ParagraphBuilder(
|
||||
ParagraphStyle(
|
||||
textAlign: TextAlign.left,
|
||||
fontSize: 16,
|
||||
textDirection: TextDirection.ltr),
|
||||
);
|
||||
builder.pushStyle(
|
||||
ui.TextStyle(color: Colors.lightGreenAccent, background: background));
|
||||
builder.addText('${barcode.displayValue}');
|
||||
builder.pop();
|
||||
textAlign: TextAlign.left,
|
||||
fontSize: 16,
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
)
|
||||
..pushStyle(
|
||||
ui.TextStyle(color: Colors.lightGreenAccent, background: background),
|
||||
)
|
||||
..addText('${barcode.displayValue}')
|
||||
..pop();
|
||||
|
||||
final left = translateX(
|
||||
barcode.boundingBox.left,
|
||||
|
|
@ -77,16 +79,16 @@ class BarcodeDetectorPainter extends CustomPainter {
|
|||
// paint,
|
||||
// );
|
||||
|
||||
final List<Offset> cornerPoints = <Offset>[];
|
||||
final cornerPoints = <Offset>[];
|
||||
for (final point in barcode.cornerPoints) {
|
||||
final double x = translateX(
|
||||
final x = translateX(
|
||||
point.x.toDouble(),
|
||||
size,
|
||||
imageSize,
|
||||
rotation,
|
||||
cameraLensDirection,
|
||||
);
|
||||
final double y = translateY(
|
||||
final y = translateY(
|
||||
point.y.toDouble(),
|
||||
size,
|
||||
imageSize,
|
||||
|
|
@ -99,20 +101,23 @@ class BarcodeDetectorPainter extends CustomPainter {
|
|||
|
||||
// Add the first point to close the polygon
|
||||
cornerPoints.add(cornerPoints.first);
|
||||
canvas.drawPoints(PointMode.polygon, cornerPoints, paint);
|
||||
|
||||
canvas.drawParagraph(
|
||||
builder.build()
|
||||
..layout(ParagraphConstraints(
|
||||
width: (right - left).abs(),
|
||||
)),
|
||||
Offset(
|
||||
canvas
|
||||
..drawPoints(PointMode.polygon, cornerPoints, paint)
|
||||
..drawParagraph(
|
||||
builder.build()
|
||||
..layout(
|
||||
ParagraphConstraints(
|
||||
width: (right - left).abs(),
|
||||
),
|
||||
),
|
||||
Offset(
|
||||
Platform.isAndroid &&
|
||||
cameraLensDirection == CameraLensDirection.front
|
||||
? right
|
||||
: left,
|
||||
top),
|
||||
);
|
||||
top,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ double translateX(
|
|||
switch (cameraLensDirection) {
|
||||
case CameraLensDirection.back:
|
||||
return x * canvasSize.width / imageSize.width;
|
||||
// ignore: no_default_cases
|
||||
default:
|
||||
return canvasSize.width - x * canvasSize.width / imageSize.width;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
|||
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
||||
import 'package:twonly/src/views/settings/settings_main.view.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
||||
import 'package:twonly/src/views/tutorial/tutorials.dart';
|
||||
|
||||
class ChatListView extends StatefulWidget {
|
||||
const ChatListView({super.key});
|
||||
|
|
@ -38,7 +37,6 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
List<Group> _groupsArchived = [];
|
||||
|
||||
GlobalKey searchForOtherUsers = GlobalKey();
|
||||
Timer? tutorial;
|
||||
bool showFeedbackShortcut = false;
|
||||
|
||||
@override
|
||||
|
|
@ -58,16 +56,6 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
});
|
||||
});
|
||||
|
||||
tutorial = Timer(const Duration(seconds: 1), () async {
|
||||
tutorial = null;
|
||||
if (!mounted) return;
|
||||
await showChatListTutorialSearchOtherUsers(context, searchForOtherUsers);
|
||||
if (!mounted) return;
|
||||
// if (_groupsNotPinned.isNotEmpty) {
|
||||
// await showChatListTutorialContextMenu(context, firstUserListItemKey);
|
||||
// }
|
||||
});
|
||||
|
||||
final changeLog = await rootBundle.loadString('CHANGELOG.md');
|
||||
final changeLogHash =
|
||||
(await compute(Sha256().hash, changeLog.codeUnits)).bytes;
|
||||
|
|
@ -97,7 +85,6 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
tutorial?.cancel();
|
||||
_contactsSub.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import 'package:twonly/src/views/components/flame.dart';
|
|||
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||
import 'package:twonly/src/views/groups/group.view.dart';
|
||||
import 'package:twonly/src/views/tutorial/tutorials.dart';
|
||||
|
||||
Color getMessageColor(Message message) {
|
||||
return (message.senderId == null)
|
||||
|
|
@ -89,7 +88,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
Message? quotesMessage;
|
||||
GlobalKey verifyShieldKey = GlobalKey();
|
||||
late FocusNode textFieldFocus;
|
||||
Timer? tutorial;
|
||||
final ItemScrollController itemScrollController = ItemScrollController();
|
||||
int? focusedScrollItem;
|
||||
|
||||
|
|
@ -99,12 +97,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
group = widget.group;
|
||||
textFieldFocus = FocusNode();
|
||||
initStreams();
|
||||
|
||||
tutorial = Timer(const Duration(seconds: 1), () async {
|
||||
tutorial = null;
|
||||
if (!mounted) return;
|
||||
await showVerifyShieldTutorial(context, verifyShieldKey);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -114,7 +106,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
contactSub?.cancel();
|
||||
groupActionsSub?.cancel();
|
||||
lastOpenedMessageByContactSub?.cancel();
|
||||
tutorial?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class HomeViewState extends State<HomeView> {
|
|||
CustomPaint? _customPaint;
|
||||
String? _text;
|
||||
|
||||
final _orientations = {
|
||||
final Map<DeviceOrientation, int> _orientations = {
|
||||
DeviceOrientation.portraitUp: 0,
|
||||
DeviceOrientation.landscapeLeft: 90,
|
||||
DeviceOrientation.portraitDown: 180,
|
||||
|
|
|
|||
|
|
@ -62,26 +62,26 @@ class _HelpViewState extends State<HelpView> {
|
|||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsResetTutorials),
|
||||
subtitle: Text(
|
||||
context.lang.settingsResetTutorialsDesc,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onTap: () async {
|
||||
await updateUserdata((user) {
|
||||
user.tutorialDisplayed = [];
|
||||
return user;
|
||||
});
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.lang.settingsResetTutorialsSuccess),
|
||||
duration: const Duration(seconds: 3),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// ListTile(
|
||||
// title: Text(context.lang.settingsResetTutorials),
|
||||
// subtitle: Text(
|
||||
// context.lang.settingsResetTutorialsDesc,
|
||||
// style: const TextStyle(fontSize: 12),
|
||||
// ),
|
||||
// onTap: () async {
|
||||
// await updateUserdata((user) {
|
||||
// user.tutorialDisplayed = [];
|
||||
// return user;
|
||||
// });
|
||||
// if (!context.mounted) return;
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(context.lang.settingsResetTutorialsSuccess),
|
||||
// duration: const Duration(seconds: 3),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(context.lang.allowErrorTracking),
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
Future<void> showTutorial(
|
||||
BuildContext context,
|
||||
List<TargetFocus> targets,
|
||||
) async {
|
||||
final completer = Completer<dynamic>();
|
||||
TutorialCoachMark(
|
||||
targets: targets,
|
||||
colorShadow: context.color.primary,
|
||||
textSkip: context.lang.ok,
|
||||
alignSkip: Alignment.bottomCenter,
|
||||
textStyleSkip: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
),
|
||||
onSkip: () {
|
||||
completer.complete();
|
||||
return true;
|
||||
},
|
||||
onFinish: () {
|
||||
completer.complete();
|
||||
},
|
||||
).show(context: context);
|
||||
|
||||
await completer.future;
|
||||
}
|
||||
|
||||
Future<bool> checkIfTutorialAlreadyShown(String tutorialId) async {
|
||||
final user = await getUser();
|
||||
if (user == null) return true;
|
||||
user.tutorialDisplayed ??= [];
|
||||
if (user.tutorialDisplayed!.contains(tutorialId)) {
|
||||
return true;
|
||||
}
|
||||
user.tutorialDisplayed!.add(tutorialId);
|
||||
|
||||
await updateUserdata((u) {
|
||||
u.tutorialDisplayed = user.tutorialDisplayed;
|
||||
return u;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TargetFocus getTargetFocus(
|
||||
BuildContext context,
|
||||
GlobalKey key,
|
||||
String title,
|
||||
String body,
|
||||
) {
|
||||
final renderBox = key.currentContext!.findRenderObject()! as RenderBox;
|
||||
final position = renderBox.localToGlobal(Offset.zero);
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
final centerY = screenHeight / 2;
|
||||
|
||||
double top = 0;
|
||||
double bottom = 0;
|
||||
|
||||
if (position.dy < centerY) {
|
||||
bottom = 0;
|
||||
top = position.dy;
|
||||
} else {
|
||||
bottom = centerY;
|
||||
top = 0;
|
||||
}
|
||||
|
||||
return TargetFocus(
|
||||
identify: title,
|
||||
keyTarget: key,
|
||||
contents: [
|
||||
TargetContent(
|
||||
align: ContentAlign.custom,
|
||||
customPosition: CustomTargetContentPosition(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
body,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/tutorial/show_tutorial.dart';
|
||||
|
||||
final lockDisplayTutorial = Mutex();
|
||||
|
||||
Future<void> showChatListTutorialSearchOtherUsers(
|
||||
BuildContext context,
|
||||
GlobalKey searchForOtherUsers,
|
||||
) async {
|
||||
await lockDisplayTutorial.protect(() async {
|
||||
if (await checkIfTutorialAlreadyShown('chat_list:search_users')) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final targets = <TargetFocus>[
|
||||
getTargetFocus(
|
||||
context,
|
||||
searchForOtherUsers,
|
||||
context.lang.tutorialChatListSearchUsersTitle,
|
||||
context.lang.tutorialChatListSearchUsersDesc,
|
||||
),
|
||||
];
|
||||
await showTutorial(context, targets);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showChatListTutorialContextMenu(
|
||||
BuildContext context,
|
||||
GlobalKey firstUserListItemKey,
|
||||
) async {
|
||||
await lockDisplayTutorial.protect(() async {
|
||||
if (await checkIfTutorialAlreadyShown('chat_list:context_menu')) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final targets = <TargetFocus>[
|
||||
getTargetFocus(
|
||||
context,
|
||||
firstUserListItemKey,
|
||||
context.lang.tutorialChatListContextMenuTitle,
|
||||
context.lang.tutorialChatListContextMenuDesc,
|
||||
),
|
||||
];
|
||||
await showTutorial(context, targets);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showVerifyShieldTutorial(
|
||||
BuildContext context,
|
||||
GlobalKey firstUserListItemKey,
|
||||
) async {
|
||||
await lockDisplayTutorial.protect(() async {
|
||||
if (await checkIfTutorialAlreadyShown('chat_messages:verify_shield')) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final targets = <TargetFocus>[
|
||||
getTargetFocus(
|
||||
context,
|
||||
firstUserListItemKey,
|
||||
context.lang.tutorialChatMessagesVerifyShieldTitle,
|
||||
context.lang.tutorialChatMessagesVerifyShieldDesc,
|
||||
),
|
||||
];
|
||||
await showTutorial(context, targets);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showReopenMediaFilesTutorial(
|
||||
BuildContext context,
|
||||
GlobalKey firstUserListItemKey,
|
||||
) async {
|
||||
await lockDisplayTutorial.protect(() async {
|
||||
if (await checkIfTutorialAlreadyShown('chat_messages:reopen_message')) {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
final targets = <TargetFocus>[
|
||||
getTargetFocus(
|
||||
context,
|
||||
firstUserListItemKey,
|
||||
context.lang.tutorialChatMessagesReopenMessageTitle,
|
||||
context.lang.tutorialChatMessagesReopenMessageDesc,
|
||||
),
|
||||
];
|
||||
await showTutorial(context, targets);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue