Compare commits

...

27 Commits

Author SHA1 Message Date
vishnukvmd
f52ee5683b v1.0.11 2022-11-14 18:57:45 +05:30
vishnukvmd
332faa9166 Send error messages to sentry, even if the error object is missing 2022-11-14 18:57:37 +05:30
vishnukvmd
3d95c789f5 v1.0.10 2022-11-14 18:33:56 +05:30
vishnukvmd
244d4d969a Enable auto-updates 2022-11-14 18:33:49 +05:30
vishnukvmd
32605dc708 Implement a functional notification service 2022-11-14 18:23:55 +05:30
vishnukvmd
ef0c302c94 Add configuration for production builds for Android 2022-11-14 18:12:07 +05:30
vishnukvmd
fba694de68 Sort dependencies 2022-11-14 18:10:06 +05:30
vishnukvmd
3d4cd106cb Add dependencies to power auto-update 2022-11-14 18:08:42 +05:30
vishnukvmd
61e6e0ffaa Define responsible disclosure policy 2022-11-14 14:00:00 +05:30
vishnukvmd
e0b952e516 Configure Sentry 2022-11-14 13:58:09 +05:30
vishnukvmd
546a9234a4 Allow resetting on 2FA with the mnemonic phrase 2022-11-13 13:57:18 +05:30
vishnukvmd
da2083ef08 Fix styles for 2FA entry 2022-11-13 13:56:49 +05:30
vishnukvmd
8a1177e7db Fix case 2022-11-13 13:29:18 +05:30
vishnukvmd
8acd1faf03 Remove link to rate 2022-11-13 12:02:45 +05:30
vishnukvmd
0c28b83b46 Fix button color 2022-11-13 11:58:40 +05:30
vishnukvmd
fa0ac608f4 Add illustration for the lock screen 2022-11-13 11:57:07 +05:30
vishnukvmd
0965b367cc Decode the recovery key into hex instead of base64 2022-11-13 11:41:40 +05:30
vishnukvmd
37db940720 v1.0.9 2022-11-12 01:19:42 +05:30
vishnukvmd
334800472e Fix iOS permission issue 2022-11-12 01:19:25 +05:30
vishnukvmd
0da4497857 v1.0.8 2022-11-12 01:07:04 +05:30
vishnukvmd
0b05a21dc7 Fix fab colors 2022-11-12 01:05:25 +05:30
vishnukvmd
2558473399 Remove unused import 2022-11-12 00:47:53 +05:30
vishnukvmd
4dd465fdfa Remove junk value 2022-11-12 00:47:35 +05:30
vishnukvmd
1f20ba17f4 Remove toast library that was blocking user interaction 2022-11-12 00:46:02 +05:30
vishnukvmd
9ce8059212 Fix ripple 2022-11-12 00:37:06 +05:30
vishnukvmd
e69a3adf14 Remove redundant import 2022-11-12 00:35:21 +05:30
vishnukvmd
8baa350056 Improve style of the code widget 2022-11-12 00:34:06 +05:30
39 changed files with 669 additions and 351 deletions

9
.vscode/launch.json vendored
View File

@@ -29,11 +29,18 @@
"args": ["--dart-define", "endpoint=http://192.168.1.30:8080"]
},
{
"name": "Prod",
"name": "iOS Prod",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": ["--target", "lib/main.dart"]
},
{
"name": "Android Prod",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": ["--target", "lib/main.dart", "--flavor", "independent"]
}
]
}

50
SECURITY.md Normal file
View File

@@ -0,0 +1,50 @@
ente believes that working with security researchers across the globe is crucial
to keeping our users safe. If you believe you've found a security issue in our
product or service, we encourage you to notify us (security@ente.io). We welcome
working with you to resolve the issue promptly. Thanks in advance!
# Disclosure Policy
- Let us know as soon as possible upon discovery of a potential security issue,
and we'll make every effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any
disclosure to the public or a third-party. We may publicly disclose the issue
before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and
interruption or degradation of our service. Only interact with accounts you
own or with explicit permission of the account holder.
- If you would like to encrypt your report, please use the PGP key with long ID
`E273695C0403F34F74171932DF6DDDE98EBD2394` (available in the public keyserver
pool).
# In-scope
- Security issues in any current release of ente. This includes the web app,
desktop app, and mobile apps (iOS and Android). Product downloads are
available at https://ente.io. Source code is available at
https://github.com/ente-io.
# Exclusions
The following bug classes are out-of scope:
- Bugs that are already reported on any of ente's issue trackers
(https://github.com/ente-io), or that we already know of. Note that some of
our issue tracking is private.
- Issues in an upstream software dependency (ex: Flutter, Next.js etc) which are
already reported to the upstream maintainer.
- Attacks requiring physical access to a user's device.
- Self-XSS
- Issues related to software or protocols not under ente's control
- Vulnerabilities in outdated versions of ente
- Missing security best practices that do not directly lead to a vulnerability
- Issues that do not have any impact on the general public
While researching, we'd like to ask you to refrain from:
- Denial of service
- Spamming
- Social engineering (including phishing) of ente staff or contractors
- Any physical attempts against ente property or data centers
Thank you for helping keep ente and our users safe!

View File

@@ -35,7 +35,7 @@
<meta-data android:name="flutterEmbedding" android:value="2"/>
<meta-data android:name="io.sentry.dsn"
android:value="https://8aeb7f013be74f829f8b73b46b3d7a80@sentry.ente.io/8"/>
android:value="https://ed4ddd6309b847ba8849935e26e9b648@sentry.ente.io/9"/>
</application>
<queries>

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -81,6 +81,11 @@ PODS:
- SDWebImage (5.13.4):
- SDWebImage/Core (= 5.13.4)
- SDWebImage/Core (5.13.4)
- Sentry/HybridSDK (7.30.2)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 7.30.2)
- share_plus (0.0.1):
- Flutter
- shared_preferences_ios (0.0.1):
@@ -111,6 +116,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
@@ -125,6 +131,7 @@ SPEC REPOS:
- OrderedSet
- Reachability
- SDWebImage
- Sentry
- SwiftyGif
- Toast
@@ -163,6 +170,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_ios/ios"
qr_code_scanner:
:path: ".symlinks/plugins/qr_code_scanner/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_ios:
@@ -185,7 +194,7 @@ SPEC CHECKSUMS:
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
flutter_sodium: c84426b4de738514b5b66cfdeb8a06634e72fe0b
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
fluttertoast: 74526702fea2c060ea55dde75895b7e1bde1c86b
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c
@@ -197,6 +206,8 @@ SPEC CHECKSUMS:
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SDWebImage: e5cc87bf736e60f49592f307bdf9e157189298a3
Sentry: 9be48e341494bc976c963b05aa4a8ca48308c684
sentry_flutter: 544e6376e35b00eef9f0864f8bb7f10a0e204993
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904

View File

@@ -58,5 +58,7 @@
<false/>
<key>NSFaceIDUsageDescription</key>
<string>Please allow auth to lock itself with FaceID or TouchID</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Please allow auth to pick a file to import data from</string>
</dict>
</plist>

View File

@@ -10,10 +10,11 @@ import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import "package:ente_auth/onboarding/view/onboarding_page.dart";
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/settings/app_update_dialog.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:flutter_easyloading/flutter_easyloading.dart';
import "package:flutter_localizations/flutter_localizations.dart";
class App extends StatefulWidget {
@@ -39,6 +40,21 @@ class _AppState extends State<App> {
setState(() {});
}
});
UpdateService.instance.shouldUpdate().then((shouldUpdate) {
if (shouldUpdate) {
Future.delayed(Duration.zero, () {
showDialog(
context: context,
builder: (BuildContext context) {
return AppUpdateDialog(
UpdateService.instance.getLatestVersionInfo(),
);
},
barrierColor: Colors.black.withOpacity(0.85),
);
});
}
});
super.initState();
}
@@ -62,7 +78,6 @@ class _AppState extends State<App> {
theme: lightTheme,
darkTheme: dartTheme,
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
supportedLocales: AppLocalizations.supportedLocales,
localizationsDelegates: const [
AppLocalizations.delegate,
@@ -80,7 +95,6 @@ class _AppState extends State<App> {
theme: lightThemeData,
darkTheme: darkThemeData,
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
supportedLocales: AppLocalizations.supportedLocales,
localizationsDelegates: const [
AppLocalizations.delegate,

View File

@@ -4,9 +4,10 @@ import 'dart:typed_data';
import 'package:bip39/bip39.dart' as bip39;
import 'package:ente_auth/core/constants.dart';
// import 'package:ente_auth/core/error-reporting/super_logging.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/event_bus.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:ente_auth/core/logging/super_logging.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import 'package:ente_auth/models/key_attributes.dart';
@@ -14,7 +15,6 @@ import 'package:ente_auth/models/key_gen_result.dart';
import 'package:ente_auth/models/private_key_attributes.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/utils/crypto_util.dart';
// import 'package:ente_auth/utils/validator_util.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
@@ -120,7 +120,7 @@ class Configuration {
}
await _migrateSecurityStorageToFirstUnlock();
}
// SuperLogging.setUserID(await _getOrCreateAnonymousUserID());
SuperLogging.setUserID(await _getOrCreateAnonymousUserID());
}
Future<void> logout({bool autoLogout = false}) async {

View File

@@ -4,9 +4,7 @@ const int thumbnailLargeSize = 512;
const int compressedThumbnailResolution = 1080;
const int thumbnailDataLimit = 100 * 1024;
const String sentryDSN =
"https://2235e5c99219488ea93da34b9ac1cb68@sentry.ente.io/4";
const String sentryDebugDSN =
"https://ca5e686dd7f149d9bf94e620564cceba@sentry.ente.io/3";
"https://ed4ddd6309b847ba8849935e26e9b648@sentry.ente.io/9";
const String sentryTunnel = "https://sentry-reporter.ente.io";
const String roadmapURL = "https://roadmap.ente.io";
const int microSecondsInDay = 86400000000;

View File

@@ -7,13 +7,16 @@ import 'dart:collection';
import 'dart:core';
import 'dart:io';
import 'package:ente_auth/core/logging/tunneled_transport.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
typedef FutureOrVoidCallback = FutureOr<void> Function();
@@ -189,17 +192,17 @@ class SuperLogging {
if (config.body == null) return;
if (enable && sentryIsEnabled) {
// await SentryFlutter.init(
// (options) {
// options.dsn = config.sentryDsn;
// options.httpClient = http.Client();
// if (config.tunnel != null) {
// options.transport =
// TunneledTransport(Uri.parse(config.tunnel), options);
// }
// },
// appRunner: () => config.body(),
// );
await SentryFlutter.init(
(options) {
options.dsn = config.sentryDsn;
options.httpClient = http.Client();
if (config.tunnel != null) {
options.transport =
TunneledTransport(Uri.parse(config.tunnel), options);
}
},
appRunner: () => config.body(),
);
} else {
await config.body();
}
@@ -207,21 +210,21 @@ class SuperLogging {
static void setUserID(String userID) async {
if (config?.sentryDsn != null) {
// Sentry.configureScope((scope) => scope.user = SentryUser(id: userID));
Sentry.configureScope((scope) => scope.user = SentryUser(id: userID));
$.info("setting sentry user ID to: $userID");
}
}
static Future<void> _sendErrorToSentry(Object error, StackTrace stack) async {
// try {
// await Sentry.captureException(
// error,
// stackTrace: stack,
// );
// } catch (e) {
// $.info('Sending report to sentry.io failed: $e');
// $.info('Original error: $error');
// }
try {
await Sentry.captureException(
error,
stackTrace: stack,
);
} catch (e) {
$.info('Sending report to sentry.io failed: $e');
$.info('Original error: $error');
}
}
static String _lastExtraLines = '';
@@ -249,8 +252,16 @@ class SuperLogging {
}
// add error to sentry queue
if (sentryIsEnabled && rec.error != null) {
_sendErrorToSentry(rec.error, null);
if (sentryIsEnabled) {
if (rec.error != null) {
_sendErrorToSentry(rec.error, null);
} else if (rec.level == Level.SEVERE || rec.level == Level.SHOUT) {
if (rec.error != null) {
_sendErrorToSentry(rec.error, null);
} else {
_sendErrorToSentry(rec.message, null);
}
}
}
}
@@ -286,16 +297,16 @@ class SuperLogging {
static Future<void> setupSentry() async {
await for (final error in sentryQueueControl.stream.asBroadcastStream()) {
// try {
// Sentry.captureException(
// error,
// );
// } catch (e) {
// $.fine(
// "sentry upload failed; will retry after ${config.sentryRetryDelay}",
// );
// doSentryRetry(error);
// }
try {
Sentry.captureException(
error,
);
} catch (e) {
$.fine(
"sentry upload failed; will retry after ${config.sentryRetryDelay}",
);
doSentryRetry(error);
}
}
}
@@ -372,6 +383,6 @@ class SuperLogging {
return false;
}
final pkgName = (await PackageInfo.fromPlatform()).packageName;
return pkgName.startsWith("io.ente.photos.fdroid");
return pkgName.endsWith("fdroid");
}
}

View File

@@ -0,0 +1,141 @@
// @dart=2.9
import 'dart:convert';
import 'package:http/http.dart';
import 'package:sentry/sentry.dart';
/// A transport is in charge of sending the event to the Sentry server.
class TunneledTransport implements Transport {
final Uri _tunnel;
final SentryOptions _options;
final Dsn _dsn;
_CredentialBuilder _credentialBuilder;
final Map<String, String> _headers;
factory TunneledTransport(Uri tunnel, SentryOptions options) {
return TunneledTransport._(tunnel, options);
}
TunneledTransport._(this._tunnel, this._options)
: _dsn = Dsn.parse(_options.dsn),
_headers = _buildHeaders(
_options.platformChecker.isWeb,
_options.sdk.identifier,
) {
_credentialBuilder = _CredentialBuilder(
_dsn,
_options.sdk.identifier,
_options.clock,
);
}
@override
Future<SentryId> send(SentryEnvelope envelope) async {
final streamedRequest = await _createStreamedRequest(envelope);
final response = await _options.httpClient
.send(streamedRequest)
.then(Response.fromStream);
if (response.statusCode != 200) {
// body guard to not log the error as it has performance impact to allocate
// the body String.
if (_options.debug) {
_options.logger(
SentryLevel.error,
'API returned an error, statusCode = ${response.statusCode}, '
'body = ${response.body}',
);
}
return const SentryId.empty();
} else {
_options.logger(
SentryLevel.debug,
'Envelope ${envelope.header.eventId ?? "--"} was sent successfully.',
);
}
final eventId = json.decode(response.body)['id'];
if (eventId == null) {
return null;
}
return SentryId.fromId(eventId);
}
Future<StreamedRequest> _createStreamedRequest(
SentryEnvelope envelope,
) async {
final streamedRequest = StreamedRequest('POST', _tunnel);
envelope
.envelopeStream(_options)
.listen(streamedRequest.sink.add)
.onDone(streamedRequest.sink.close);
streamedRequest.headers.addAll(_credentialBuilder.configure(_headers));
return streamedRequest;
}
}
class _CredentialBuilder {
final String _authHeader;
final ClockProvider _clock;
int get timestamp => _clock().millisecondsSinceEpoch;
_CredentialBuilder._(String authHeader, ClockProvider clock)
: _authHeader = authHeader,
_clock = clock;
factory _CredentialBuilder(
Dsn dsn,
String sdkIdentifier,
ClockProvider clock,
) {
final authHeader = _buildAuthHeader(
publicKey: dsn.publicKey,
secretKey: dsn.secretKey,
sdkIdentifier: sdkIdentifier,
);
return _CredentialBuilder._(authHeader, clock);
}
static String _buildAuthHeader({
String publicKey,
String secretKey,
String sdkIdentifier,
}) {
var header = 'Sentry sentry_version=7, sentry_client=$sdkIdentifier, '
'sentry_key=$publicKey';
if (secretKey != null) {
header += ', sentry_secret=$secretKey';
}
return header;
}
Map<String, String> configure(Map<String, String> headers) {
return headers
..addAll(
<String, String>{
'X-Sentry-Auth': '$_authHeader, sentry_timestamp=$timestamp'
},
);
}
}
Map<String, String> _buildHeaders(bool isWeb, String sdkIdentifier) {
final headers = {'Content-Type': 'application/x-sentry-envelope'};
// NOTE(lejard_h) overriding user agent on VM and Flutter not sure why
// for web it use browser user agent
if (!isWeb) {
headers['User-Agent'] = sdkIdentifier;
}
return headers;
}

View File

@@ -224,6 +224,14 @@ extension CustomColorScheme on ColorScheme {
Color get inverseBackgroundColor =>
brightness != Brightness.light ? backgroundBaseLight : backgroundBaseDark;
Color get fabForegroundColor => brightness == Brightness.light
? const Color.fromRGBO(255, 255, 255, 1)
: const Color.fromRGBO(40, 40, 40, 1);
Color get fabBackgroundColor => brightness != Brightness.light
? const Color.fromRGBO(255, 255, 255, 1)
: const Color.fromRGBO(40, 40, 40, 1);
Color get defaultTextColor =>
brightness == Brightness.light ? textBaseLight : textBaseDark;
@@ -344,6 +352,10 @@ extension CustomColorScheme on ColorScheme {
? Colors.black.withOpacity(0.32)
: Colors.black.withOpacity(0.64);
Color get codeCardBackgroundColor => brightness == Brightness.light
? const Color.fromRGBO(246, 246, 246, 1)
: const Color.fromRGBO(40, 40, 40, 0.6);
EnteTheme get enteTheme =>
brightness == Brightness.light ? lightTheme : darkTheme;
}

View File

@@ -1,11 +1,13 @@
// @dart=2.9
import "package:ente_auth/app/view/app.dart";
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/logging/super_logging.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/billing_service.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/services/user_remote_flag_service.dart';
import 'package:ente_auth/services/user_service.dart';
@@ -29,6 +31,7 @@ Future<void> _runInForeground() async {
return await _runWithLogs(() async {
_logger.info("Starting app in foreground");
await _init(false, via: 'mainMethod');
UpdateService.instance.showUpdateNotification();
runApp(
AppLock(
builder: (args) => const App(),
@@ -47,6 +50,8 @@ Future _runWithLogs(Function() function, {String prefix = ""}) async {
body: function,
logDirPath: (await getApplicationSupportDirectory()).path + "/logs",
maxLogFiles: 5,
sentryDsn: sentryDSN,
tunnel: sentryTunnel,
enableInDebugMode: true,
prefix: prefix,
),
@@ -61,7 +66,8 @@ Future<void> _init(bool bool, {String via}) async {
await Network.instance.init();
await UserService.instance.init();
await UserRemoteFlagService.instance.init();
await UpdateService.instance.init();
await AuthenticatorService.instance.init();
await BillingService.instance.init();
await NotificationService.instance.init();
await UpdateService.instance.init();
}

View File

@@ -2,7 +2,6 @@ import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
class SetupEnterSecretKeyPage extends StatefulWidget {
@@ -15,8 +14,7 @@ class SetupEnterSecretKeyPage extends StatefulWidget {
class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
final _accountController = TextEditingController();
final _secretController =
TextEditingController(text: kDebugMode ? "JBSWY3DPEHPK3PXP" : "");
final _secretController = TextEditingController(text: "");
@override
Widget build(BuildContext context) {

View File

@@ -1,53 +1,56 @@
// import 'dart:io';
//
// import 'package:flutter_local_notifications/flutter_local_notifications.dart';
//
// class NotificationService {
// static final NotificationService instance =
// NotificationService._privateConstructor();
//
// NotificationService._privateConstructor();
// final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
// FlutterLocalNotificationsPlugin();
//
// Future<void> init() async {
// if (!Platform.isAndroid) {
// return;
// }
// const AndroidInitializationSettings initializationSettingsAndroid =
// AndroidInitializationSettings('notification_icon');
// const InitializationSettings initializationSettings =
// InitializationSettings(
// android: initializationSettingsAndroid,
// );
// await _flutterLocalNotificationsPlugin.initialize(
// initializationSettings,
// onSelectNotification: selectNotification,
// );
// }
//
// Future selectNotification(String? payload) async {}
//
// Future<void> showNotification(String title, String message) async {
// if (!Platform.isAndroid) {
// return;
// }
// const AndroidNotificationDetails androidPlatformChannelSpecifics =
// AndroidNotificationDetails(
// 'io.ente.photos',
// 'ente',
// channelDescription: 'ente alerts',
// importance: Importance.max,
// priority: Priority.high,
// showWhen: false,
// );
// const NotificationDetails platformChannelSpecifics =
// NotificationDetails(android: androidPlatformChannelSpecifics);
// await _flutterLocalNotificationsPlugin.show(
// 0,
// title,
// message,
// platformChannelSpecifics,
// );
// }
// }
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationService {
static final NotificationService instance =
NotificationService._privateConstructor();
NotificationService._privateConstructor();
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> init() async {
if (!Platform.isAndroid) {
return;
}
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('notification_icon');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
);
await _flutterLocalNotificationsPlugin.initialize(
initializationSettings,
);
final implementation =
_flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
if (implementation != null) {
implementation.requestPermission();
}
}
Future<void> showNotification(String title, String message) async {
if (!Platform.isAndroid) {
return;
}
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'io.ente.auth',
'auth',
channelDescription: 'auth alerts',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await _flutterLocalNotificationsPlugin.show(
0,
title,
message,
platformChannelSpecifics,
);
}
}

View File

@@ -2,7 +2,9 @@
import 'dart:io';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -55,33 +57,33 @@ class UpdateService {
return _latestVersion;
}
// Future<void> showUpdateNotification() async {
// if (!isIndependent()) {
// return;
// }
// final shouldUpdate = await this.shouldUpdate();
// final lastNotificationShownTime =
// _prefs.getInt(kUpdateAvailableShownTimeKey) ?? 0;
// final now = DateTime.now().microsecondsSinceEpoch;
// final hasBeen3DaysSinceLastNotification =
// (now - lastNotificationShownTime) > (3 * microSecondsInDay);
// if (shouldUpdate &&
// hasBeen3DaysSinceLastNotification &&
// _latestVersion.shouldNotify) {
// NotificationService.instance.showNotification(
// "update available",
// "click to install our best version yet",
// );
// await _prefs.setInt(kUpdateAvailableShownTimeKey, now);
// } else {
// _logger.info("Debouncing notification");
// }
// }
Future<void> showUpdateNotification() async {
if (!isIndependent()) {
return;
}
final shouldUpdate = await this.shouldUpdate();
final lastNotificationShownTime =
_prefs.getInt(kUpdateAvailableShownTimeKey) ?? 0;
final now = DateTime.now().microsecondsSinceEpoch;
final hasBeen3DaysSinceLastNotification =
(now - lastNotificationShownTime) > (3 * microSecondsInDay);
if (shouldUpdate &&
hasBeen3DaysSinceLastNotification &&
_latestVersion.shouldNotify) {
NotificationService.instance.showNotification(
"Update available",
"Click to install our best version yet",
);
await _prefs.setInt(kUpdateAvailableShownTimeKey, now);
} else {
_logger.info("Debouncing notification");
}
}
Future<LatestVersionInfo> _getLatestVersionInfo() async {
final response = await Network.instance
.getDio()
.get("https://ente.io/release-info/independent.json");
.get("https://ente.io/release-info/auth-independent.json");
return LatestVersionInfo.fromMap(response.data["latestVersion"]);
}
@@ -89,18 +91,7 @@ class UpdateService {
if (Platform.isIOS) {
return false;
}
if (!kDebugMode &&
_packageInfo.packageName != "io.ente.auth.independent") {
return false;
}
return true;
}
bool isIndependentFlavor() {
if (Platform.isIOS) {
return false;
}
return _packageInfo.packageName.startsWith("io.ente.auth.independent");
return kDebugMode || _packageInfo.packageName.endsWith("independent");
}
}

View File

@@ -1,7 +1,9 @@
// @dart=2.9
import 'package:bip39/bip39.dart' as bip39;
import 'package:dio/dio.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/events/user_details_changed_event.dart';
@@ -620,6 +622,14 @@ class UserService {
await dialog.show();
String secret;
try {
if (recoveryKey.contains(' ')) {
if (recoveryKey.split(' ').length != mnemonicKeyWordCount) {
throw AssertionError(
'recovery code should have $mnemonicKeyWordCount words',
);
}
recoveryKey = bip39.mnemonicToEntropy(recoveryKey);
}
secret = Sodium.bin2base64(
await CryptoUtil.decrypt(
Sodium.base642bin(encryptedSecret),

View File

@@ -61,7 +61,7 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
child: const Text(
"Verify",
style: TextStyle(
color: Colors.green,
color: Colors.purple,
),
),
onPressed: () {

View File

@@ -68,7 +68,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
);
} catch (e) {
await dialog.hide();
String errMessage = 'the recovery key you entered is incorrect';
String errMessage = 'The recovery key you entered is incorrect';
if (e is AssertionError) {
errMessage = '$errMessage : ${e.message}';
}

View File

@@ -1,13 +1,13 @@
import 'dart:async';
import 'package:clipboard/clipboard.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animation_progress_bar/flutter_animation_progress_bar.dart';
// import 'package:flutter_animation_progress_bar/flutter_animation_progress_bar.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class CodeWidget extends StatefulWidget {
@@ -48,86 +48,107 @@ class _CodeWidgetState extends State<CodeWidget> {
@override
Widget build(BuildContext context) {
return Slidable(
key: ValueKey(widget.code.hashCode),
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: _onDeletePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor: const Color(0xFFFE4A49),
icon: Icons.delete,
label: 'Delete',
),
],
),
child: InkWell(
onTap: () {
FlutterClipboard.copy(_getTotp())
.then((value) => showToast(context, "Copied to clipboard"));
},
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
FAProgressBar(
currentValue: _timeRemaining / widget.code.period * 100,
size: 4,
animatedDuration: const Duration(milliseconds: 200),
progressColor: Colors.orange,
changeColorValue: 40,
changeProgressColor: Colors.green,
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Text(
Uri.decodeFull(widget.code.issuer),
style: Theme.of(context).textTheme.headline6,
return Container(
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8),
child: Slidable(
key: ValueKey(widget.code.hashCode),
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: _onDeletePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor: const Color(0xFFFE4A49),
icon: Icons.delete,
label: 'Delete',
padding: const EdgeInsets.only(left: 0, right: 0),
),
],
),
child: Container(
margin: const EdgeInsets.only(right: 10),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
child: Material(
color: Colors.transparent,
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
onTap: () {
FlutterClipboard.copy(_getTotp()).then(
(value) => showToast(context, "Copied to clipboard"),
);
},
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
FAProgressBar(
currentValue:
_timeRemaining / widget.code.period * 100,
size: 4,
animatedDuration: const Duration(milliseconds: 200),
progressColor: Colors.orange,
changeColorValue: 40,
changeProgressColor: Colors.green,
),
const SizedBox(
height: 16,
),
Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Text(
Uri.decodeFull(widget.code.issuer),
style: Theme.of(context).textTheme.headline6,
),
),
Container(
padding: const EdgeInsets.only(right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"next",
style: Theme.of(context).textTheme.caption,
),
],
),
),
Container(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Text(
_getTotp(),
style: const TextStyle(fontSize: 24),
),
),
Text(
_getNextTotp(),
style: const TextStyle(
fontSize: 24,
color: Colors.grey,
),
),
],
),
),
const SizedBox(
height: 20,
),
],
),
),
),
),
Container(
padding: const EdgeInsets.only(right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"next",
style: Theme.of(context).textTheme.caption,
),
],
),
),
Container(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Text(
_getTotp(),
style: const TextStyle(fontSize: 24),
),
),
Text(
_getNextTotp(),
style: const TextStyle(
fontSize: 24,
color: Colors.grey,
),
),
],
),
),
const SizedBox(
height: 32,
),
],
),
),
),
),
@@ -195,16 +216,4 @@ class _CodeWidgetState extends State<CodeWidget> {
return "Error";
}
}
Color _getProgressColor() {
final progress = _timeRemaining / widget.code.period;
if (progress > 0.6) {
return Colors.green;
} else if (progress > 0.4) {
return Colors.yellow;
} else if (progress > 2) {
return Colors.orange;
}
return Colors.red;
}
}

View File

@@ -152,8 +152,8 @@ class _HomePageState extends State<HomePage> {
childPadding: const EdgeInsets.all(5),
spaceBetweenChildren: 4,
tooltip: 'Add Code',
foregroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.inverseBackgroundColor,
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
overlayOpacity: 0.5,
overlayColor: Theme.of(context).colorScheme.background,
elevation: 8.0,
@@ -161,16 +161,16 @@ class _HomePageState extends State<HomePage> {
children: [
SpeedDialChild(
child: const Icon(Icons.qr_code),
foregroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.inverseBackgroundColor,
label: 'Scan a QR Code',
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
labelWidget: const SpeedDialLabelWidget("Scan a QR Code"),
onTap: _redirectToScannerPage,
),
SpeedDialChild(
child: const Icon(Icons.keyboard),
foregroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.inverseBackgroundColor,
label: 'Enter details manually',
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
labelWidget: const SpeedDialLabelWidget("Enter details manually"),
onTap: _redirectToManualEntryPage,
),
],
@@ -224,3 +224,30 @@ class _HomePageState extends State<HomePage> {
);
}
}
class SpeedDialLabelWidget extends StatelessWidget {
final String label;
const SpeedDialLabelWidget(
this.label, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(4),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.fabBackgroundColor,
),
child: Text(
label,
style: TextStyle(
color: Theme.of(context).colorScheme.fabForegroundColor,
),
),
);
}
}

View File

@@ -48,7 +48,7 @@ class AccountSectionWidget extends StatelessWidget {
String recoveryKey;
try {
recoveryKey =
Sodium.bin2base64(Configuration.instance.getRecoveryKey());
Sodium.bin2hex(Configuration.instance.getRecoveryKey());
} catch (e) {
showGenericErrorDialog(context);
return;

View File

@@ -1,12 +1,12 @@
// @dart=2.9
// import 'package:open_file/open_file.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:open_file/open_file.dart';
class AppUpdateDialog extends StatefulWidget {
final LatestVersionInfo latestVersionInfo;
@@ -25,7 +25,12 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
changelog.add(
Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 0, 4),
child: Text("- " + log, style: Theme.of(context).textTheme.caption),
child: Text(
"- " + log,
style: Theme.of(context).textTheme.caption.copyWith(
fontSize: 14,
),
),
),
);
}
@@ -152,7 +157,7 @@ class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
},
);
Navigator.of(context, rootNavigator: true).pop('dialog');
// OpenFile.open(_saveUrl);
OpenFile.open(_saveUrl);
} catch (e) {
Logger("ApkDownloader").severe(e);
final AlertDialog alert = AlertDialog(

View File

@@ -1,8 +1,5 @@
// @dart=2.9
import 'dart:io';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
@@ -33,19 +30,6 @@ class SocialSectionWidget extends StatelessWidget {
const SocialsMenuItemWidget("Reddit", "https://reddit.com/r/enteio"),
sectionOptionSpacing,
];
if (!UpdateService.instance.isIndependent()) {
options.addAll(
[
SocialsMenuItemWidget(
"Rate us! ✨",
Platform.isAndroid
? "https://play.google.com/store/apps/details?id=io.ente.photos"
: "https://apps.apple.com/in/app/ente-photos/id1542026904",
),
sectionOptionSpacing,
],
);
}
return Column(children: options);
}
}

View File

@@ -34,7 +34,7 @@ class _LockScreenState extends State<LockScreen> {
alignment: Alignment.center,
children: [
Opacity(
opacity: 0.2,
opacity: 0.3,
child: Image.asset('assets/loading_photos_background.png'),
),
SizedBox(

View File

@@ -20,10 +20,6 @@ class TwoFactorAuthenticationPage extends StatefulWidget {
class _TwoFactorAuthenticationPageState
extends State<TwoFactorAuthenticationPage> {
final _pinController = TextEditingController();
final _pinPutDecoration = BoxDecoration(
border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)),
borderRadius: BorderRadius.circular(15.0),
);
String _code = "";
LifecycleEventHandler _lifecycleEventHandler;
@@ -62,6 +58,16 @@ class _TwoFactorAuthenticationPageState
}
Widget _getBody() {
final pinPutDecoration = BoxDecoration(
border: Border.all(
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder
.borderSide
.color,
),
borderRadius: BorderRadius.circular(15.0),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
@@ -89,15 +95,12 @@ class _TwoFactorAuthenticationPageState
});
},
controller: _pinController,
submittedFieldDecoration: _pinPutDecoration.copyWith(
submittedFieldDecoration: pinPutDecoration.copyWith(
borderRadius: BorderRadius.circular(20.0),
),
selectedFieldDecoration: _pinPutDecoration,
followingFieldDecoration: _pinPutDecoration.copyWith(
selectedFieldDecoration: pinPutDecoration,
followingFieldDecoration: pinPutDecoration.copyWith(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: const Color.fromRGBO(45, 194, 98, 0.5),
),
),
inputDecoration: const InputDecoration(
focusedBorder: InputBorder.none,

View File

@@ -1,8 +1,5 @@
import 'dart:io';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:fluttertoast/fluttertoast.dart';
Future showToast(
@@ -11,31 +8,16 @@ Future showToast(
toastLength = Toast.LENGTH_LONG,
iOSDismissOnTap = true,
}) async {
if (Platform.isAndroid) {
await Fluttertoast.cancel();
return Fluttertoast.showToast(
msg: message,
toastLength: toastLength,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Theme.of(context).colorScheme.toastBackgroundColor,
textColor: Theme.of(context).colorScheme.toastTextColor,
fontSize: 16.0,
);
} else {
EasyLoading.instance
..backgroundColor = Theme.of(context).colorScheme.toastBackgroundColor
..indicatorColor = Theme.of(context).colorScheme.toastBackgroundColor
..textColor = Theme.of(context).colorScheme.toastTextColor
..userInteractions = true
..loadingStyle = EasyLoadingStyle.custom;
return EasyLoading.showToast(
message,
duration: Duration(seconds: (toastLength == Toast.LENGTH_LONG ? 5 : 2)),
toastPosition: EasyLoadingToastPosition.bottom,
dismissOnTap: iOSDismissOnTap,
);
}
await Fluttertoast.cancel();
return Fluttertoast.showToast(
msg: message,
toastLength: toastLength,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Theme.of(context).colorScheme.toastBackgroundColor,
textColor: Theme.of(context).colorScheme.toastTextColor,
fontSize: 16.0,
);
}
Future<void> showShortToast(context, String message) {

View File

@@ -7,12 +7,16 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <sentry_flutter/sentry_flutter_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin");
sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_linux
sentry_flutter
url_launcher_linux
)

View File

@@ -6,9 +6,11 @@ import FlutterMacOS
import Foundation
import connectivity_macos
import flutter_local_notifications
import flutter_secure_storage_macos
import package_info_plus_macos
import path_provider_macos
import sentry_flutter
import share_plus_macos
import shared_preferences_macos
import sqflite
@@ -16,9 +18,11 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))

View File

@@ -260,6 +260,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.3"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.4"
device_info:
dependency: "direct main"
description:
@@ -384,13 +391,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.1"
flutter_easyloading:
dependency: "direct main"
description:
name: flutter_easyloading
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
flutter_email_sender:
dependency: "direct main"
description:
@@ -412,6 +412,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.3"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "12.0.3"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
@@ -494,13 +515,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.2.0"
flutter_spinkit:
dependency: transitive
description:
name: flutter_spinkit
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -524,7 +538,7 @@ packages:
name: fluttertoast
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.9"
version: "8.1.1"
frontend_server_client:
dependency: transitive
description:
@@ -714,6 +728,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
open_file:
dependency: "direct main"
description:
name: open_file
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
otp:
dependency: "direct main"
description:
@@ -924,6 +945,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
sentry:
dependency: "direct main"
description:
name: sentry
url: "https://pub.dartlang.org"
source: hosted
version: "6.15.1"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "6.15.1"
share_plus:
dependency: "direct main"
description:
@@ -1174,6 +1209,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.16"
timezone:
dependency: transitive
description:
name: timezone
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0"
timing:
dependency: transitive
description:

View File

@@ -1,6 +1,6 @@
name: ente_auth
description: ente two-factor authenticator
version: 1.0.7+7
version: 1.0.11+11
publish_to: none
environment:
@@ -8,64 +8,63 @@ environment:
dependencies:
adaptive_theme: ^3.1.0 # done
bloc: ^8.0.3 #done
bip39: ^1.0.6 #done
bloc: ^8.0.3 #done
clipboard: ^0.1.3
collection: # dart
computer: ^2.0.0
confetti: ^0.7.0
connectivity: ^3.0.3
cupertino_icons: ^1.0.0
device_info: ^2.0.2
dio: ^4.0.6
dotted_border: ^2.0.0+2
email_validator: ^2.0.1
event_bus: ^2.0.0
dio: ^4.0.6
expandable: ^5.0.1
expansion_tile_card: ^2.0.0
file_picker: ^4.6.1
fk_user_agent: ^2.1.0
flutter:
sdk: flutter
flutter_animation_progress_bar: ^2.2.1
flutter_bloc: ^8.0.1
flutter_email_sender: ^5.1.0
flutter_inappwebview: ^5.7.1
flutter_launcher_icons: ^0.9.3
dotted_border: ^2.0.0+2
in_app_purchase: ^0.5.2
flutter_secure_storage: ^6.0.0
flutter_animation_progress_bar: ^2.2.1
flutter_slidable: ^2.0.0
file_picker: ^4.6.1
flutter:
sdk: flutter
flutter_bloc: ^8.0.1
flutter_native_splash: ^2.2.13
local_auth: ^1.1.5
pinput: ^1.2.2
password_strength: ^0.2.0
flutter_sodium: ^0.2.0
flutter_windowmanager: ^0.2.0
flutter_local_notifications: ^12.0.3
flutter_localizations:
sdk: flutter
# sentry:
# path: thirdparty/sentry-dart/dart
# sentry_flutter:
# path: thirdparty/sentry-dart/flutter
json_annotation: ^4.5.0
fluttertoast: ^8.0.6
flutter_native_splash: ^2.2.13
flutter_secure_storage: ^6.0.0
flutter_slidable: ^2.0.0
flutter_sodium: ^0.2.0
flutter_speed_dial: ^6.2.0
flutter_windowmanager: ^0.2.0
fluttertoast: ^8.1.1
google_nav_bar: ^5.0.5 #supported
http: ^0.13.4
move_to_background: ^1.0.2
otp: ^3.1.1
path_provider: ^2.0.11
in_app_purchase: ^0.5.2
intl: ^0.17.0
qr_code_scanner: ^1.0.1
sqflite: ^2.1.0
share_plus: ^4.4.0
package_info_plus: ^1.0.1
shared_preferences: ^2.0.5
flutter_easyloading: ^3.0.5
uuid: ^3.0.4
url_launcher: ^6.1.5
json_annotation: ^4.5.0
local_auth: ^1.1.5
logging: ^1.0.1
move_to_background: ^1.0.2
open_file: ^3.2.1
otp: ^3.1.1
package_info_plus: ^1.0.1
password_strength: ^0.2.0
path_provider: ^2.0.11
pinput: ^1.2.2
qr_code_scanner: ^1.0.1
sentry: ^6.12.1
sentry_flutter: ^6.12.1
share_plus: ^4.4.0
shared_preferences: ^2.0.5
sqflite: ^2.1.0
step_progress_indicator: ^1.0.2
confetti: ^0.7.0
clipboard: ^0.1.3
flutter_speed_dial: ^6.2.0
url_launcher: ^6.1.5
uuid: ^3.0.4
dev_dependencies:
bloc_test: ^9.0.3

View File

@@ -7,11 +7,14 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <sentry_flutter/sentry_flutter_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
SentryFlutterPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
sentry_flutter
url_launcher_windows
)