Compare commits
276 Commits
ua741-patc
...
optimize-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29c67bd5dd | ||
|
|
da15842597 | ||
|
|
eb959046cd | ||
|
|
1178ae26d0 | ||
|
|
4a2f5b4676 | ||
|
|
9fe15d7ff0 | ||
|
|
28a2afe275 | ||
|
|
c072097c11 | ||
|
|
0fece5666b | ||
|
|
26e564aec2 | ||
|
|
dd420a80a4 | ||
|
|
3dc0620e18 | ||
|
|
173d075f8b | ||
|
|
48283282e5 | ||
|
|
fa555c448f | ||
|
|
6f6770d677 | ||
|
|
0b894e9724 | ||
|
|
0670550cb1 | ||
|
|
45783cf527 | ||
|
|
1615779eb8 | ||
|
|
02e4c9d8fd | ||
|
|
6eab6457ee | ||
|
|
25490a7238 | ||
|
|
f519ff8a51 | ||
|
|
afebe1ade1 | ||
|
|
3862644dd5 | ||
|
|
274a7d207d | ||
|
|
add2f0c8de | ||
|
|
8e807616e0 | ||
|
|
70f4325c71 | ||
|
|
38ea2248b8 | ||
|
|
9600b26359 | ||
|
|
5b3e996aaa | ||
|
|
4d4cce091f | ||
|
|
aaca140d1b | ||
|
|
596ffcd4c4 | ||
|
|
41ef85a294 | ||
|
|
f722d82835 | ||
|
|
cbb3096534 | ||
|
|
f635e1e856 | ||
|
|
c6734a5cb7 | ||
|
|
e26b4796d3 | ||
|
|
99c0194c0f | ||
|
|
e824c02d7f | ||
|
|
a11f66b51d | ||
|
|
f202fef266 | ||
|
|
ff8cfd3e87 | ||
|
|
431ab7fcc7 | ||
|
|
2ac1d58dac | ||
|
|
5533e6a71d | ||
|
|
a8ae0727a8 | ||
|
|
6955788724 | ||
|
|
f6dd35f5e7 | ||
|
|
9148916d88 | ||
|
|
68545f8947 | ||
|
|
76c5c12c53 | ||
|
|
8d749a2dc8 | ||
|
|
56af818482 | ||
|
|
fc1096c985 | ||
|
|
88260a05e3 | ||
|
|
4b5f91a428 | ||
|
|
29b12fc6b5 | ||
|
|
c3eec71d60 | ||
|
|
2fccdee0d6 | ||
|
|
8f053e7a7b | ||
|
|
de7291f5d4 | ||
|
|
e09c952198 | ||
|
|
28a842b006 | ||
|
|
19eb342f59 | ||
|
|
2b82c79be9 | ||
|
|
86a9dee49d | ||
|
|
33b1a7e4f8 | ||
|
|
de783e91dc | ||
|
|
554c8f4b7a | ||
|
|
f438142646 | ||
|
|
aa6c010562 | ||
|
|
2830c89bde | ||
|
|
4de530b882 | ||
|
|
62d7c87dc7 | ||
|
|
3a1b6dbf15 | ||
|
|
58182cc8ab | ||
|
|
5e0bba390b | ||
|
|
df6a3b94db | ||
|
|
72b7e12768 | ||
|
|
3a7d82a799 | ||
|
|
5f1cfb9ba5 | ||
|
|
298e3695c7 | ||
|
|
621713d0b4 | ||
|
|
34813d2fae | ||
|
|
bb177bc3f6 | ||
|
|
d8e4418d78 | ||
|
|
9771a5bc5d | ||
|
|
65f7e3f6c6 | ||
|
|
49b9d83f05 | ||
|
|
273d7bd00a | ||
|
|
4e8991dc10 | ||
|
|
aa4207f878 | ||
|
|
3176ba8a93 | ||
|
|
fcf038c4d8 | ||
|
|
46aad76039 | ||
|
|
9a654988f8 | ||
|
|
1ce749e93e | ||
|
|
db88432b9d | ||
|
|
354bcc715f | ||
|
|
5541198967 | ||
|
|
3e2dbe2c1b | ||
|
|
5d3d18f347 | ||
|
|
9a876f3d59 | ||
|
|
3b7600ae7b | ||
|
|
8bacf8ff93 | ||
|
|
b356e5d0a5 | ||
|
|
777516446d | ||
|
|
37c1d0f6a8 | ||
|
|
be1bf28cd8 | ||
|
|
de5f0fbb39 | ||
|
|
48fa2bd6de | ||
|
|
df584f34e9 | ||
|
|
0241ad1fe5 | ||
|
|
435ed212c6 | ||
|
|
1e9a6a1e14 | ||
|
|
a7ba3079cb | ||
|
|
97f1bb71c7 | ||
|
|
cad8613e81 | ||
|
|
b46e51f64d | ||
|
|
e6bf64548c | ||
|
|
5729e0cf3e | ||
|
|
b353539328 | ||
|
|
f2049ac7fa | ||
|
|
e82ba882d6 | ||
|
|
eecd7ed355 | ||
|
|
bc70b4e725 | ||
|
|
a52cebf0e5 | ||
|
|
4c31a7bcd6 | ||
|
|
cf938eca91 | ||
|
|
deef13ece9 | ||
|
|
a3d3ee24f8 | ||
|
|
6b37cc46a5 | ||
|
|
3132373c26 | ||
|
|
5b4ff1d01a | ||
|
|
20d8a42239 | ||
|
|
b19814dd2c | ||
|
|
ad39694026 | ||
|
|
72ede1a109 | ||
|
|
5fe86858ef | ||
|
|
7546d8cad2 | ||
|
|
a74908214d | ||
|
|
94e398dc89 | ||
|
|
a5a19581fc | ||
|
|
ea409fc266 | ||
|
|
0e9d7106f7 | ||
|
|
6fe89fdc0e | ||
|
|
193e1374e1 | ||
|
|
e11a6ace80 | ||
|
|
9033bd8cec | ||
|
|
085551b5a7 | ||
|
|
210a0a45c1 | ||
|
|
ee035681ab | ||
|
|
cc2d65d796 | ||
|
|
0e61b3dfd4 | ||
|
|
0a3035e5d5 | ||
|
|
7adb1c0a6c | ||
|
|
cb55be1e5c | ||
|
|
d7a7144b33 | ||
|
|
e6a867a859 | ||
|
|
a9c8e4476f | ||
|
|
f914263b2f | ||
|
|
6f94d91afb | ||
|
|
57569e79fe | ||
|
|
b695db80ab | ||
|
|
ae1a43b8bf | ||
|
|
d9d9acfa3e | ||
|
|
d430936ae8 | ||
|
|
b02acc579f | ||
|
|
e87be4b9af | ||
|
|
399148aa59 | ||
|
|
abd733934b | ||
|
|
661e1f92d5 | ||
|
|
97b36681dc | ||
|
|
5dd5756a41 | ||
|
|
d2cfa374bd | ||
|
|
fb64c8aa4c | ||
|
|
86b54e2241 | ||
|
|
c33396ea60 | ||
|
|
24f48b5054 | ||
|
|
094c92c8b6 | ||
|
|
c2922a0cb2 | ||
|
|
9a357a716d | ||
|
|
5574fd748e | ||
|
|
f2be25667f | ||
|
|
053d0cfcaa | ||
|
|
0ff8184f47 | ||
|
|
f971835ae7 | ||
|
|
694e3ca121 | ||
|
|
be850c27c6 | ||
|
|
3aaf11ba1d | ||
|
|
3019d858c9 | ||
|
|
81e926ef2d | ||
|
|
016d646971 | ||
|
|
1f100566ad | ||
|
|
d69595e744 | ||
|
|
7a91e714fe | ||
|
|
e352de8b9c | ||
|
|
e36831b599 | ||
|
|
df74c7d54d | ||
|
|
d7986b5c7c | ||
|
|
3e888876d1 | ||
|
|
6af494206e | ||
|
|
93f32de8c1 | ||
|
|
e0b3e6464e | ||
|
|
c67d2f0836 | ||
|
|
49106a3dd9 | ||
|
|
b61c75dc84 | ||
|
|
465fc7c7d3 | ||
|
|
487e4ef559 | ||
|
|
34bf4f6bba | ||
|
|
af36978ede | ||
|
|
a375dfdc2e | ||
|
|
a5b0e66e9d | ||
|
|
2b3b7a5e2a | ||
|
|
b1dc9272a0 | ||
|
|
7c33c160b2 | ||
|
|
be232efbc6 | ||
|
|
bbac09b4a6 | ||
|
|
a4626ae7a1 | ||
|
|
e8551df8b9 | ||
|
|
77cb40aef4 | ||
|
|
827090fb24 | ||
|
|
39b9670fcc | ||
|
|
d227a2bf20 | ||
|
|
0b766415a4 | ||
|
|
3b727549d5 | ||
|
|
dbdf19ee8d | ||
|
|
2e3d621077 | ||
|
|
0455481f3d | ||
|
|
c9a7918397 | ||
|
|
957f0bc041 | ||
|
|
24f5a5813a | ||
|
|
860b2895f6 | ||
|
|
a510320d0e | ||
|
|
de04f18cb2 | ||
|
|
b84b73fda2 | ||
|
|
12b0618149 | ||
|
|
28a43393f9 | ||
|
|
21b3bdf204 | ||
|
|
3436fb7fb1 | ||
|
|
045b40b2b2 | ||
|
|
c09922d1a3 | ||
|
|
3e9032588e | ||
|
|
14e570b676 | ||
|
|
a412aa4886 | ||
|
|
a7a162d375 | ||
|
|
b444bdc5ec | ||
|
|
f9299e7950 | ||
|
|
8a9f73ada5 | ||
|
|
d5c1970ca2 | ||
|
|
f8aff3e12b | ||
|
|
490759243b | ||
|
|
e8a9e509a8 | ||
|
|
c392ad5dcb | ||
|
|
9bb084d610 | ||
|
|
00b05e2d7c | ||
|
|
93736fe57a | ||
|
|
995ae50418 | ||
|
|
42e6dff0f5 | ||
|
|
150513d3e5 | ||
|
|
13ed1e76bc | ||
|
|
a7d0e2eef5 | ||
|
|
134314c285 | ||
|
|
eb3e3db8e6 | ||
|
|
3b9b886ae9 | ||
|
|
d88df36c3d | ||
|
|
62d7311780 | ||
|
|
9d76ccc173 | ||
|
|
1752192688 | ||
|
|
873ee3ac14 | ||
|
|
cfce2d00f5 |
@@ -142,6 +142,22 @@ var _updateFreeUserStorage = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var _sendMail = &cobra.Command{
|
||||
Use: "send-mail <to-email> <from-email> <from-name>",
|
||||
Args: cobra.ExactArgs(3),
|
||||
Short: "Sends a test mail via the admin api",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
recoverWithLog()
|
||||
var flags = &model.AdminActionForUser{}
|
||||
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||
if f.Name == "admin-user" {
|
||||
flags.AdminEmail = f.Value.String()
|
||||
}
|
||||
})
|
||||
return ctrl.SendTestMail(context.Background(), *flags, args[0], args[1], args[2])
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(_adminCmd)
|
||||
_ = _userDetailsCmd.MarkFlagRequired("admin-user")
|
||||
@@ -159,5 +175,6 @@ func init() {
|
||||
_updateFreeUserStorage.Flags().StringP("user", "u", "", "The email of the user to update subscription for. (required)")
|
||||
// add a flag with no value --no-limit
|
||||
_updateFreeUserStorage.Flags().String("no-limit", "True", "When true, sets 100TB as storage limit, and expiry to current date + 100 years")
|
||||
_adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _disablePasskeyCmd, _updateFreeUserStorage, _listUsers, _deleteUser)
|
||||
_sendMail.Flags().StringP("admin-user", "a", "", "The email of the admin user. ")
|
||||
_adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _disablePasskeyCmd, _updateFreeUserStorage, _listUsers, _deleteUser, _sendMail)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ente-io/cli/pkg"
|
||||
"github.com/spf13/cobra/doc"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/ente-io/cli/pkg"
|
||||
"github.com/spf13/cobra/doc"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -20,11 +21,6 @@ var ctrl *pkg.ClICtrl
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "ente",
|
||||
Short: "CLI tool for exporting your photos from ente.io",
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Sprintf("Hello World")
|
||||
},
|
||||
}
|
||||
|
||||
func GenerateDocs() error {
|
||||
|
||||
@@ -139,5 +139,28 @@ func (c *Client) UpdateFreePlanSub(ctx context.Context, userDetails *models.User
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) SendTestMail(ctx context.Context, toEmail, fromEmail, fromName string) error {
|
||||
body := map[string]interface{}{
|
||||
"to": []string{toEmail},
|
||||
"fromName": fromName,
|
||||
"fromEmail": fromEmail,
|
||||
"subject": "Test mail from Ente",
|
||||
"body": "This is a test mail from Ente",
|
||||
}
|
||||
r, err := c.restClient.R().
|
||||
SetContext(ctx).
|
||||
SetBody(body).
|
||||
Post("/admin/mail")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.IsError() {
|
||||
return &ApiError{
|
||||
StatusCode: r.StatusCode(),
|
||||
Message: r.String(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -156,6 +156,23 @@ func (c *ClICtrl) UpdateFreeStorage(ctx context.Context, params model.AdminActio
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClICtrl) SendTestMail(ctx context.Context, params model.AdminActionForUser, to, from, fromName string) error {
|
||||
accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.Client.SendTestMail(accountCtx, to, from, fromName)
|
||||
if err != nil {
|
||||
if apiErr, ok := err.(*api.ApiError); ok && apiErr.StatusCode == 400 && strings.Contains(apiErr.Message, "Token is too old") {
|
||||
fmt.Printf("Error: old admin token, please re-authenticate using `ente account add` \n")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully sent test email to %s\n", to)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClICtrl) buildAdminContext(ctx context.Context, adminEmail string) (context.Context, error) {
|
||||
accounts, err := c.GetAccounts(ctx)
|
||||
if err != nil {
|
||||
|
||||
0
docs/docs/cli/index.md
Normal file
0
docs/docs/cli/index.md
Normal file
@@ -63,11 +63,20 @@ It has no relation to Backblaze, Wasabi or Scaleway.
|
||||
Each bucket's endpoint, region, key and secret should be configured accordingly
|
||||
if using an external bucket.
|
||||
|
||||
A sample configuration for `b2-eu-cen` is provided, which can be used for other
|
||||
2 buckets as well:
|
||||
If a bucket has SSL support enabled, set `s3.are_local_buckets` to `false`. Enable path-style URL by setting `s3.use_path_style_urls` to `true`.
|
||||
|
||||
::: note
|
||||
|
||||
You can configure this for individual buckets over defining top-level configuration if you are using the latest server image (August 2025)
|
||||
|
||||
:::
|
||||
|
||||
A sample configuration for `b2-eu-cen` is provided, which can be used for other 2 buckets as well:
|
||||
|
||||
```yaml
|
||||
b2-eu-cen:
|
||||
are_local_buckets: true
|
||||
use_path_style_urls: true
|
||||
key: <key>
|
||||
secret: <secret>
|
||||
endpoint: localhost:3200
|
||||
|
||||
@@ -96,8 +96,8 @@ provide correct credentials for proper connectivity within Museum.
|
||||
The `s3` section within `museum.yaml` is by default configured to use local
|
||||
MinIO buckets when using `quickstart.sh` or Docker Compose.
|
||||
|
||||
If you wish to use an external S3 provider, you can edit the configuration with
|
||||
your provider's credentials, and set `s3.are_local_buckets` to `false`.
|
||||
If you wish to use an external S3 provider with SSL, you can edit the configuration with
|
||||
your provider's credentials, and set `s3.are_local_buckets` to `false`. Additionally, you can configure this for specific buckets in the corresponding bucket sections in the Compose file.
|
||||
|
||||
If you are using default MinIO, it is accessible at port `3200`. Web Console can
|
||||
be accessed by enabling port `3201` in the Compose file.
|
||||
@@ -111,11 +111,11 @@ and [troubleshooting](/self-hosting/troubleshooting/uploads) sections.
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------------------------- | -------------------------------------------- | ------- |
|
||||
| `s3.b2-eu-cen` | Primary hot storage S3 config | |
|
||||
| `s3.b2-eu-cen` | Primary hot storage bucket configuration | |
|
||||
| `s3.wasabi-eu-central-2-v3.compliance` | Whether to disable compliance lock on delete | `true` |
|
||||
| `s3.scw-eu-fr-v3` | Optional secondary S3 config | |
|
||||
| `s3.wasabi-eu-central-2-derived` | Derived data storage | |
|
||||
| `s3.are_local_buckets` | Use local MinIO-compatible storage | `false` |
|
||||
| `s3.scw-eu-fr-v3` | Cold storage bucket configuration | |
|
||||
| `s3.wasabi-eu-central-2-v3` | Secondary hot storage configuration | |
|
||||
| `s3.are_local_buckets` | | `true` |
|
||||
| `s3.use_path_style_urls` | Enable path-style URLs for MinIO | `false` |
|
||||
|
||||
### Encryption Keys
|
||||
@@ -171,6 +171,8 @@ smtp:
|
||||
email:
|
||||
# Optional name for sender
|
||||
sender-name:
|
||||
# Optional encryption
|
||||
encryption:
|
||||
```
|
||||
|
||||
| Variable | Description | Default |
|
||||
@@ -181,6 +183,7 @@ smtp:
|
||||
| `smtp.password` | SMTP auth password | |
|
||||
| `smtp.email` | Sender email address | |
|
||||
| `smtp.sender-name` | Custom name for email sender | |
|
||||
| `smtp.encryption` | Encryption method (tls, ssl) | |
|
||||
| `transmail.key` | Zeptomail API key | |
|
||||
|
||||
### WebAuthn Passkey Support
|
||||
|
||||
45
mobile/.gitignore
vendored
Normal file
45
mobile/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
melos_*.iml
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
72
mobile/analysis_options.yaml
Normal file
72
mobile/analysis_options.yaml
Normal file
@@ -0,0 +1,72 @@
|
||||
# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html
|
||||
# or https://pub.dev/packages/lint (Effective dart)
|
||||
# use "flutter analyze ." or "dart analyze ." for running lint checks
|
||||
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
linter:
|
||||
rules:
|
||||
# Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml
|
||||
# Ref https://dart-lang.github.io/linter/lints/
|
||||
- avoid_print
|
||||
- avoid_unnecessary_containers
|
||||
- avoid_web_libraries_in_flutter
|
||||
- no_logic_in_create_state
|
||||
- prefer_const_constructors
|
||||
- prefer_const_constructors_in_immutables
|
||||
- prefer_const_declarations
|
||||
- prefer_const_literals_to_create_immutables
|
||||
- prefer_final_locals
|
||||
- require_trailing_commas
|
||||
- sized_box_for_whitespace
|
||||
- use_full_hex_values_for_flutter_colors
|
||||
- use_key_in_widget_constructors
|
||||
- cancel_subscriptions
|
||||
|
||||
|
||||
- avoid_empty_else
|
||||
- exhaustive_cases
|
||||
|
||||
# just style suggestions
|
||||
- sort_pub_dependencies
|
||||
- use_rethrow_when_possible
|
||||
- prefer_double_quotes
|
||||
- directives_ordering
|
||||
- always_use_package_imports
|
||||
- sort_child_properties_last
|
||||
- unawaited_futures
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
avoid_empty_else: error
|
||||
exhaustive_cases: error
|
||||
curly_braces_in_flow_control_structures: error
|
||||
directives_ordering: error
|
||||
require_trailing_commas: error
|
||||
always_use_package_imports: warning
|
||||
prefer_final_fields: error
|
||||
unused_import: error
|
||||
camel_case_types: error
|
||||
prefer_is_empty: warning
|
||||
use_rethrow_when_possible: info
|
||||
unused_field: warning
|
||||
use_key_in_widget_constructors: warning
|
||||
sort_child_properties_last: warning
|
||||
sort_pub_dependencies: warning
|
||||
library_private_types_in_public_api: warning
|
||||
constant_identifier_names: ignore
|
||||
prefer_const_constructors: warning
|
||||
prefer_const_declarations: warning
|
||||
prefer_const_constructors_in_immutables: warning
|
||||
prefer_final_locals: warning
|
||||
unnecessary_const: error
|
||||
cancel_subscriptions: error
|
||||
unrelated_type_equality_checks: error
|
||||
unnecessary_cast: info
|
||||
|
||||
|
||||
unawaited_futures: warning # convert to warning after fixing existing issues
|
||||
invalid_dependency: info
|
||||
use_build_context_synchronously: ignore # experimental lint, requires many changes
|
||||
prefer_interpolation_to_compose_strings: ignore # later too many warnings
|
||||
prefer_double_quotes: ignore # too many warnings
|
||||
avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides
|
||||
@@ -34,6 +34,9 @@ android {
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
// Flag to enable support for the new language APIs
|
||||
coreLibraryDesugaringEnabled true
|
||||
// Sets Java compatibility to Java 8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -56,7 +59,7 @@ android {
|
||||
applicationId "io.ente.auth"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion 21
|
||||
minSdkVersion 22
|
||||
targetSdkVersion 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
@@ -115,4 +118,7 @@ flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {}
|
||||
dependencies {
|
||||
// For AGP 7.4+
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
}
|
||||
|
||||
6
mobile/apps/auth/android/app/proguard-rules.pro
vendored
Normal file
6
mobile/apps/auth/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Please add these rules to your existing keep rules in order to suppress warnings.
|
||||
# This is generated automatically by the Android Gradle plugin.
|
||||
-dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue
|
||||
-dontwarn com.google.errorprone.annotations.CheckReturnValue
|
||||
-dontwarn com.google.errorprone.annotations.Immutable
|
||||
-dontwarn com.google.errorprone.annotations.RestrictedApi
|
||||
@@ -6,6 +6,19 @@ allprojects {
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
|
||||
subprojects {
|
||||
afterEvaluate { project ->
|
||||
if (project.hasProperty('android')) {
|
||||
project.android {
|
||||
if (namespace == null) {
|
||||
namespace project.group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
|
||||
@@ -19,8 +19,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
id "com.android.application" version "8.6.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
@@ -382,6 +382,11 @@
|
||||
{
|
||||
"title": "CoinDCX"
|
||||
},
|
||||
{
|
||||
"title": "CoinTracking",
|
||||
"slug": "cointracking",
|
||||
"altNames": ["cointracking.info", "Coin Tracking"]
|
||||
},
|
||||
{
|
||||
"title": "colorado",
|
||||
"altNames": [
|
||||
@@ -735,6 +740,11 @@
|
||||
{
|
||||
"title": "Hivelocity"
|
||||
},
|
||||
{
|
||||
"title": "HRDocumentBox",
|
||||
"slug": "hrdocumentbox",
|
||||
"altNames": ["HRDocumentBox", "HR Document Box"]
|
||||
},
|
||||
{
|
||||
"title": "HSA Bank",
|
||||
"slug": "hsa_bank",
|
||||
@@ -1040,6 +1050,13 @@
|
||||
"MistralAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Mobile01",
|
||||
"slug": "mobile01",
|
||||
"altNames": [
|
||||
"M01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Mozilla"
|
||||
},
|
||||
@@ -1773,6 +1790,11 @@
|
||||
"uollet.com.br"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "VHV",
|
||||
"slug": "vhv",
|
||||
"altNames": ["VHV", "VHV Versicherung"]
|
||||
},
|
||||
{
|
||||
"title": "Vikunja"
|
||||
},
|
||||
|
||||
27
mobile/apps/auth/assets/custom-icons/icons/cointracking.svg
Normal file
27
mobile/apps/auth/assets/custom-icons/icons/cointracking.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1220.8 227.9" style="enable-background:new 0 0 1220.8 227.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0D253E;}
|
||||
.st1{fill:#008AFB;}
|
||||
</style>
|
||||
<title>CoinTracking light</title>
|
||||
<g id="Layer_2_1_">
|
||||
<g id="Layer_1-2">
|
||||
<path class="st0" d="M198.1,167c-30.2,0-54.7-24.5-54.7-54.7s24.5-54.7,54.7-54.7s54.7,24.5,54.7,54.7l0,0 C252.8,142.5,228.3,167,198.1,167z M198.1,81.6c-17,0-30.7,13.7-30.7,30.7s13.7,30.7,30.7,30.7s30.7-13.7,30.7-30.7l0,0 C228.8,95.4,215,81.6,198.1,81.6z"/>
|
||||
<path class="st0" d="M292.2,59.5h-23.6v107.9h23.6V59.5z"/>
|
||||
<path class="st0" d="M339.5,167.4h-23.8V59.5h23.8v16.2c6.2-12.5,21-18.5,33-18.5c26.1,0,41.1,16.9,41.1,47.3v62.8h-23.8v-60.1 c0-17.1-8.8-26.8-22.6-26.8c-14.1,0-27.7,7.6-27.7,28.9V167.4z"/>
|
||||
<path class="st1" d="M390.6,8.2h151.7v22.9h-65.9v136.3h-25V31.1h-60.9V8.2H390.6z"/>
|
||||
<path class="st1" d="M540.3,167.4h-23.8V59.5h23.8V79c7.4-13.5,15.4-19.5,29.6-21.8c7.5-1.2,15.7,1.8,19.2,4.2l-3.9,22 c-4.9-2.5-10.4-3.8-15.9-3.7c-20.3,0-28.9,20.3-28.9,49L540.3,167.4L540.3,167.4z"/>
|
||||
<path class="st1" d="M680.7,151.9c-7.2,11.8-22.9,17.8-36.3,17.8c-29.1,0-54.8-21.9-54.8-56.4s25.6-56.1,54.8-56.1 c12.9,0,28.9,5.3,36.3,17.5V59.5h23.6v107.9h-23.6V151.9z M647.2,146.6c17.6,0,33.3-12.2,33.3-33.5s-17.1-32.8-33.3-32.8 c-18,0-33,12.9-33,32.8S629.2,146.6,647.2,146.6L647.2,146.6z"/>
|
||||
<path class="st1" d="M778.4,57.2c17.1,0,32.6,6.7,42.7,18.7l-18.5,14.8c-5.8-6.7-14.8-10.4-24.3-10.4c-18,0-34,12.7-34,32.8 s15.9,33.7,34,33.7c9.5,0,18.5-3.9,24.3-10.6l18.7,14.6c-10.2,12-25.6,18.9-43,18.9c-31,0-57.5-22.4-57.5-56.6 S747.4,57.2,778.4,57.2z"/>
|
||||
<path class="st1" d="M860.3,117.2v50.1h-23.6V0.8h23.6V95l34.2-35.6h31.9l-46,46.9l56.4,61h-30.5L860.3,117.2z"/>
|
||||
<path class="st1" d="M967.4,59.5h-23.6v107.9h23.6V59.5z"/>
|
||||
<path class="st1" d="M1014.7,167.4h-23.8V59.5h23.8v16.2c6.2-12.5,21-18.5,33-18.5c26.1,0,41.1,16.9,41.1,47.3v62.8H1065v-60.1 c0-17.1-8.8-26.8-22.6-26.8c-14.1,0-27.7,7.6-27.7,28.9V167.4z"/>
|
||||
<path class="st1" d="M1220.2,111.5c0-12.3-4.5-24.2-12.5-33.6l13-17.1l-19.6-13l-11.9,15.7c-8.3-4.1-17.5-6.2-26.8-6.2 c-31.9,0-57.8,24.4-57.8,54.3s25.9,54.3,57.8,54.3c20.2,0,34.2,10.2,34.2,19.3s-14.1,19.3-34.2,19.3s-34.2-10.2-34.2-19.3h-23.6 c0,24,25.4,42.9,57.8,42.9s57.8-18.8,57.8-42.9c0-13.2-7.7-24.8-19.9-32.6C1212.5,142.5,1220.2,127.8,1220.2,111.5z M1128.2,111.5 c0-16.9,15.4-30.7,34.2-30.7s34.2,13.8,34.2,30.7s-15.4,30.7-34.2,30.7S1128.2,128.4,1128.2,111.5L1128.2,111.5z"/>
|
||||
<path class="st0" d="M81.4,170.5C36.4,170.5,0,134,0,89c0-21.6,8.6-42.3,23.9-57.6c31.8-31.8,83.4-31.8,115.2,0l0,0l-17.7,17.7 C99.3,27,63.6,27,41.5,49.1s-22.1,57.8,0,79.9s57.8,22.1,79.9,0l17.7,17.7C123.8,162,103.1,170.6,81.4,170.5z"/>
|
||||
<circle class="st0" cx="280.7" cy="15.5" r="15.5"/>
|
||||
<circle class="st1" cx="954.7" cy="15.5" r="15.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 18 KiB |
26
mobile/apps/auth/assets/custom-icons/icons/mobile01.svg
Normal file
26
mobile/apps/auth/assets/custom-icons/icons/mobile01.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="320" height="280" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<g id="Layer1000">
|
||||
<g id="Layer1002">
|
||||
<g id="Layer1003">
|
||||
<path d="m123.08,34.29c-66.43,0 -120.27,53.85 -120.27,120.27c0,66.43 53.85,120.27 120.27,120.27c66.47,0 120.32,-53.85 120.32,-120.27c0,-66.43 -53.85,-120.27 -120.32,-120.27zm0,215.67c-52.67,0 -95.36,-42.73 -95.36,-95.4c0,-52.67 42.68,-95.4 95.36,-95.4c52.72,0 95.4,42.73 95.4,95.4c0,52.67 -42.68,95.4 -95.4,95.4z" fill="#2a5e00" fill-rule="evenodd" id="path7"/>
|
||||
<g id="Layer1004">
|
||||
<g id="Layer1005">
|
||||
<path d="m138.72,146.29l59.61,-41.47l7.78,33.7l-67.39,7.78z" fill="#2a5e00" fill-rule="evenodd" id="path8"/>
|
||||
<path d="m110.88,146.29l-59.61,-41.47l-7.78,33.7l67.39,7.78z" fill="#2a5e00" fill-rule="evenodd" id="path9"/>
|
||||
</g>
|
||||
<path d="m43.95,192.02l74.62,49.75l87.12,-78.8l-161.75,29.05z" fill="#2a5e00" fill-rule="evenodd" id="path10"/>
|
||||
</g>
|
||||
<path d="m94.24,59.29l-30.48,-55.1l54.26,33.24l-23.79,21.86z" fill="#2a5e00" fill-rule="evenodd" id="path11"/>
|
||||
<path d="m202.64,78.1l30.43,-55.1l-54.22,33.24l23.79,21.86z" fill="#2a5e00" fill-rule="evenodd" id="path12"/>
|
||||
</g>
|
||||
<path d="m275.63,274.67l29.35,0l0,-240.76l-29.35,0l0,240.76z" fill="#2a5e00" fill-rule="evenodd" id="path13"/>
|
||||
<path d="m317.94,125.93c0,15.3 -12.33,27.63 -27.63,27.63c-15.26,0 -27.63,-12.33 -27.63,-27.63c0,-15.26 12.37,-27.63 27.63,-27.63c15.3,0 27.63,12.37 27.63,27.63z" fill="#2a5e00" fill-rule="evenodd" id="path14"/>
|
||||
<path d="m288.84,33.91l-41.76,0l16.76,45.99l23.58,0l1.42,-45.99z" fill="#2a5e00" fill-rule="evenodd" id="path15"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
27
mobile/apps/auth/assets/custom-icons/icons/vhv.svg
Normal file
27
mobile/apps/auth/assets/custom-icons/icons/vhv.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="1024" height="357" viewBox="-1.98252 -1.98252 201.02104 70.04904" id="svg3349">
|
||||
<defs id="defs3351"/>
|
||||
<path d="m 0,0 11.76,0 4.455,31.962 0.12,0 L 20.789,0 32.55,0 23.402,42.417 9.147,42.417 0,0" id="path3131" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 34.039,0 11.05,0 0,15.567 6.771,0 0,-15.567 11.05,0 0,42.417 -11.05,0 0,-17.465 -6.771,0 0,17.465 -11.05,0 0,-42.417" id="path3133" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 64.395,0 11.757,0 4.457,31.962 0.121,0 L 85.185,0 96.944,0 87.797,42.417 73.54,42.417 64.395,0" id="path3135" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 97.576,42.414 8.907,0 9.222,-42.41 -8.912,0 -9.217,42.41" id="path3137" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 111.026,42.424 8.908,-0.01 9.218,-42.41 -8.913,0 -9.213,42.42" id="path3139" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 124.564,42.414 8.906,0 9.217,-42.41 -8.906,0 -9.217,42.41" id="path3141" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 4.169,46.784 5.251,0 1.985,14.261 0.05,0 1.99,-14.261 5.247,0 -4.082,18.928 -6.363,0 -4.082,-18.928" id="path3143" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 19.356,46.784 11.216,0 0,4.031 -6.282,0 0,3.234 5.882,0 0,3.87 -5.882,0 0,3.763 6.517,0 0,4.03 -11.451,0 0,-18.928" id="path3145" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 54.93,52.247 0,-0.45 c 0,-1.117 -0.449,-2.043 -1.405,-2.043 -1.06,0 -1.538,0.823 -1.538,1.67 0,3.739 8.059,1.91 8.059,8.828 0,4.027 -2.357,5.832 -6.705,5.832 -4.082,0 -6.362,-1.404 -6.362,-5.331 l 0,-0.663 4.772,0 0,0.453 c 0,1.615 0.663,2.202 1.614,2.202 1.007,0 1.594,-0.798 1.594,-1.831 0,-3.739 -7.743,-1.88 -7.743,-8.586 0,-3.819 2.043,-5.913 6.205,-5.913 4.294,0 6.12,1.775 6.12,5.832 l -4.611,0" id="path3147" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 61.476,65.712 4.932,0 0,-18.928 -4.932,0 0,18.928 z" id="path3149" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 32.477,46.784 7.714,0 c 3.766,0 5.195,2.124 5.195,5.039 0,2.516 -0.979,4.16 -3.232,4.506 2.385,0.265 3.103,1.776 3.103,4.294 l 0,1.616 c 0,0.981 0,2.251 0.237,2.626 0.132,0.209 0.237,0.422 0.558,0.582 l 0,0.265 -5.25,0 C 40.325,64.705 40.325,62.9 40.325,62.109 l 0,-1.275 c 0,-2.147 -0.423,-2.704 -1.619,-2.704 l -1.296,0 0,7.582 -4.933,0 0,-18.928 z m 4.933,8.008 0.98,0 c 1.405,0 2.066,-0.9 2.066,-2.252 0,-1.541 -0.609,-2.202 -2.094,-2.202 l -0.952,0 0,4.454" id="path3151" style="fill:#1e1e1e;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
|
||||
<path d="m 75.975,52.54 c 0,-2.147 -0.396,-2.786 -1.35,-2.786 -1.513,0 -1.67,1.381 -1.67,6.494 0,5.117 0.157,6.497 1.67,6.497 1.22,0 1.485,-1.063 1.485,-4.64 l 4.771,0 0,1.405 c 0,5.3 -3.101,6.574 -6.256,6.574 -5.54,0 -6.76,-2.784 -6.76,-9.836 0,-7.236 1.642,-9.833 6.76,-9.833 4.451,0 6.12,2.334 6.12,5.992 l 0,1.19 -4.77,0 0,-1.057" id="path3153" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 82.524,46.784 4.928,0 0,6.948 3.02,0 0,-6.948 4.932,0 0,18.928 -4.932,0 0,-7.793 -3.02,0 0,7.793 -4.928,0 0,-18.928" id="path3155" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 97.606,46.784 11.21,0 0,4.031 -6.282,0 0,3.234 5.886,0 0,3.87 -5.886,0 0,3.763 6.522,0 0,4.03 -11.45,0 0,-18.928" id="path3157" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 130.526,46.784 0,13.655 c 0,1.824 0.559,2.306 1.485,2.306 0.928,0 1.485,-0.482 1.485,-2.306 l 0,-13.655 4.929,0 0,12.408 c 0,5.298 -2.28,6.892 -6.414,6.892 -4.135,0 -6.412,-1.594 -6.412,-6.892 l 0,-12.408 4.927,0" id="path3159" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 140.415,46.784 5.51,0 3.185,11.665 0.05,0 0,-11.665 4.614,0 0,18.928 -5.407,0 -3.288,-11.689 -0.05,0 0,11.689 -4.613,0 0,-18.928" id="path3161" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 110.726,46.784 7.716,0 c 3.76,0 5.192,2.124 5.192,5.039 0,2.516 -0.979,4.16 -3.234,4.506 2.383,0.265 3.101,1.776 3.101,4.294 l 0,1.616 c 0,0.981 0,2.251 0.238,2.626 0.131,0.209 0.24,0.422 0.558,0.582 l 0,0.265 -5.251,0 c -0.477,-1.007 -0.477,-2.812 -0.477,-3.603 l 0,-1.275 c 0,-2.147 -0.422,-2.704 -1.612,-2.704 l -1.301,0 0,7.582 -4.93,0 0,-18.928 z m 4.93,8.008 0.979,0 c 1.404,0 2.07,-0.9 2.07,-2.252 0,-1.541 -0.61,-2.202 -2.095,-2.202 l -0.954,0 0,4.454" id="path3163" style="fill:#1e1e1e;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
|
||||
<path d="m 162.334,55.428 6.361,0 0,10.284 -3.34,0 -0.113,-1.669 -0.05,0 c -0.662,1.617 -2.412,2.041 -4.082,2.041 -5.01,0 -5.459,-3.58 -5.459,-9.836 0,-6.336 1.22,-9.833 7.047,-9.833 3.502,0 5.993,1.775 5.993,6.36 l -4.771,0 c 0,-0.952 -0.07,-1.695 -0.266,-2.198 -0.185,-0.53 -0.554,-0.823 -1.139,-0.823 -1.612,0 -1.774,1.381 -1.774,6.494 0,5.117 0.161,6.497 1.67,6.497 1.034,0 1.639,-0.666 1.669,-3.978 l -1.75,0 0,-3.339" id="path3165" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 170.577,46.784 11.213,0 0,4.031 -6.28,0 0,3.234 5.885,0 0,3.87 -5.885,0 0,3.763 6.519,0 0,4.03 -11.452,0 0,-18.928" id="path3167" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
<path d="m 183.697,46.784 5.514,0 3.18,11.665 0.05,0 0,-11.665 4.615,0 0,18.928 -5.408,0 -3.284,-11.689 -0.05,0 0,11.689 -4.614,0 0,-18.928" id="path3169" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
|
||||
</svg>
|
||||
<!-- version: 20110311, original size: 197.056 66.084, border: 3% -->
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -6,14 +6,14 @@
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "debug",
|
||||
"program": "auth/lib/main.dart",
|
||||
"program": "mobile/apps/auth/lib/main.dart",
|
||||
"args": ["--dart-define", "endpoint=http://localhost:8080"]
|
||||
},
|
||||
{
|
||||
"name": "Auth Android Dev",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"program": "auth/lib/main.dart",
|
||||
"program": "mobile/apps/auth/lib/main.dart",
|
||||
"args": [
|
||||
"--dart-define",
|
||||
"endpoint=http://192.168.1.3:8080",
|
||||
@@ -25,21 +25,21 @@
|
||||
"name": "Auth iOS Dev",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"program": "auth/lib/main.dart",
|
||||
"program": "mobile/apps/auth/lib/main.dart",
|
||||
"args": ["--dart-define", "endpoint=http://192.168.1.30:8080"]
|
||||
},
|
||||
{
|
||||
"name": "Auth iOS Prod",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"program": "auth/lib/main.dart",
|
||||
"program": "mobile/apps/auth/lib/main.dart",
|
||||
"args": ["--target", "lib/main.dart"]
|
||||
},
|
||||
{
|
||||
"name": "Auth Android Prod",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"program": "auth/lib/main.dart",
|
||||
"program": "mobile/apps/auth/lib/main.dart",
|
||||
"args": ["--target", "lib/main.dart", "--flavor", "independent"]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,7 +3,6 @@ PODS:
|
||||
- Flutter
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- cupertino_http (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -40,6 +39,8 @@ PODS:
|
||||
- DKPhotoGallery/Resource (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- ente_qr (0.0.1):
|
||||
- Flutter
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
@@ -61,13 +62,12 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_native_splash (0.0.1):
|
||||
- flutter_native_splash (2.4.3):
|
||||
- Flutter
|
||||
- flutter_secure_storage (6.0.0):
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- Toast
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -87,9 +87,9 @@ PODS:
|
||||
- qr_code_scanner (0.2.0):
|
||||
- Flutter
|
||||
- MTBBarcodeScanner
|
||||
- SDWebImage (5.21.0):
|
||||
- SDWebImage/Core (= 5.21.0)
|
||||
- SDWebImage/Core (5.21.0)
|
||||
- SDWebImage (5.21.1):
|
||||
- SDWebImage/Core (= 5.21.1)
|
||||
- SDWebImage/Core (5.21.1)
|
||||
- Sentry/HybridSDK (8.46.0)
|
||||
- sentry_flutter (8.14.2):
|
||||
- Flutter
|
||||
@@ -102,37 +102,43 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- sodium_libs (2.2.1):
|
||||
- Flutter
|
||||
- sqflite (0.0.3):
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.46.1+1)":
|
||||
- "sqlite3/common (= 3.46.1+1)"
|
||||
- "sqlite3/common (3.46.1+1)"
|
||||
- "sqlite3/dbstatvtab (3.46.1+1)":
|
||||
- sqlite3 (3.50.2):
|
||||
- sqlite3/common (= 3.50.2)
|
||||
- sqlite3/common (3.50.2)
|
||||
- sqlite3/dbstatvtab (3.50.2):
|
||||
- sqlite3/common
|
||||
- "sqlite3/fts5 (3.46.1+1)":
|
||||
- sqlite3/fts5 (3.50.2):
|
||||
- sqlite3/common
|
||||
- "sqlite3/perf-threadsafe (3.46.1+1)":
|
||||
- sqlite3/math (3.50.2):
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.46.1+1)":
|
||||
- sqlite3/perf-threadsafe (3.50.2):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.50.2):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- "sqlite3 (~> 3.46.0+1)"
|
||||
- FlutterMacOS
|
||||
- sqlite3 (~> 3.50.1)
|
||||
- sqlite3/dbstatvtab
|
||||
- sqlite3/fts5
|
||||
- sqlite3/math
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- SwiftyGif (5.4.5)
|
||||
- Toast (4.1.1)
|
||||
- ua_client_hints (1.4.1):
|
||||
- Flutter
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- ente_qr (from `.symlinks/plugins/ente_qr/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- file_saver (from `.symlinks/plugins/file_saver/ios`)
|
||||
- fk_user_agent (from `.symlinks/plugins/fk_user_agent/ios`)
|
||||
@@ -155,8 +161,9 @@ DEPENDENCIES:
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sodium_libs (from `.symlinks/plugins/sodium_libs/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
- ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
@@ -169,17 +176,18 @@ SPEC REPOS:
|
||||
- Sentry
|
||||
- sqlite3
|
||||
- SwiftyGif
|
||||
- Toast
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: ".symlinks/plugins/app_links/ios"
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/darwin"
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
cupertino_http:
|
||||
:path: ".symlinks/plugins/cupertino_http/darwin"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
ente_qr:
|
||||
:path: ".symlinks/plugins/ente_qr/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
file_saver:
|
||||
@@ -224,51 +232,54 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
sodium_libs:
|
||||
:path: ".symlinks/plugins/sodium_libs/ios"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/darwin"
|
||||
sqflite_darwin:
|
||||
:path: ".symlinks/plugins/sqflite_darwin/darwin"
|
||||
sqlite3_flutter_libs:
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
|
||||
ua_client_hints:
|
||||
:path: ".symlinks/plugins/ua_client_hints/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
|
||||
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
|
||||
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
|
||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||
ente_qr: f39434aa69ea0e71047b49316365b2737f8a20aa
|
||||
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
|
||||
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
|
||||
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
||||
local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de
|
||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
sqlite3: 3e82a2daae39ba3b41ae6ee84a130494585460fc
|
||||
sqlite3_flutter_libs: 2c48c4ee7217fd653251975e43412250d5bcbbe2
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: 78f002751f1a8f65042b8da97902ba4124271c5a
|
||||
|
||||
@@ -2,20 +2,21 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
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/l10n/l10n.dart';
|
||||
import 'package:ente_auth/locale.dart';
|
||||
import "package:ente_auth/onboarding/view/onboarding_page.dart";
|
||||
import 'package:ente_auth/services/authenticator_service.dart';
|
||||
import 'package:ente_auth/services/update_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/services/window_listener_service.dart';
|
||||
import 'package:ente_auth/ui/home_page.dart';
|
||||
import 'package:ente_auth/ui/settings/app_update_dialog.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:ente_events/models/signed_in_event.dart';
|
||||
import 'package:ente_events/models/signed_out_event.dart';
|
||||
import 'package:ente_strings/l10n/strings_localizations.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
@@ -131,6 +132,7 @@ class _AppState extends State<App>
|
||||
localeListResolutionCallback: localResolutionCallBack,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
StringsLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
@@ -150,6 +152,7 @@ class _AppState extends State<App>
|
||||
localeListResolutionCallback: localResolutionCallBack,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
StringsLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
|
||||
@@ -1,95 +1,36 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' as io;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:ente_auth/core/constants.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/endpoint_updated_event.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';
|
||||
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/directory_utils.dart';
|
||||
import 'package:ente_base/models/database.dart';
|
||||
import 'package:ente_configuration/base_configuration.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class Configuration {
|
||||
class Configuration extends BaseConfiguration {
|
||||
Configuration._privateConstructor();
|
||||
|
||||
static final Configuration instance = Configuration._privateConstructor();
|
||||
static const endpoint = String.fromEnvironment(
|
||||
"endpoint",
|
||||
defaultValue: kDefaultProductionEndpoint,
|
||||
);
|
||||
static const emailKey = "email";
|
||||
static const keyAttributesKey = "key_attributes";
|
||||
|
||||
static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time";
|
||||
static const keyKey = "key";
|
||||
static const secretKeyKey = "secret_key";
|
||||
static const authSecretKeyKey = "auth_secret_key";
|
||||
static const offlineAuthSecretKey = "offline_auth_secret_key";
|
||||
static const tokenKey = "token";
|
||||
static const encryptedTokenKey = "encrypted_token";
|
||||
static const userIDKey = "user_id";
|
||||
static const hasMigratedSecureStorageKey = "has_migrated_secure_storage";
|
||||
static const hasOptedForOfflineModeKey = "has_opted_for_offline_mode";
|
||||
static const endPointKey = "endpoint";
|
||||
final List<String> onlineSecureKeys = [
|
||||
keyKey,
|
||||
secretKeyKey,
|
||||
authSecretKeyKey,
|
||||
];
|
||||
|
||||
final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds;
|
||||
|
||||
static final _logger = Logger("Configuration");
|
||||
|
||||
String? _cachedToken;
|
||||
late SharedPreferences _preferences;
|
||||
String? _key;
|
||||
String? _secretKey;
|
||||
String? _authSecretKey;
|
||||
String? _offlineAuthKey;
|
||||
late FlutterSecureStorage _secureStorage;
|
||||
late String _tempDirectory;
|
||||
|
||||
String? _volatilePassword;
|
||||
|
||||
Future<void> init() async {
|
||||
@override
|
||||
Future<void> init(List<EnteBaseDatabase> dbs) async {
|
||||
await super.init(dbs);
|
||||
_preferences = await SharedPreferences.getInstance();
|
||||
sqfliteFfiInit();
|
||||
_secureStorage = const FlutterSecureStorage(
|
||||
iOptions: IOSOptions(
|
||||
accessibility: KeychainAccessibility.first_unlock_this_device,
|
||||
),
|
||||
);
|
||||
_tempDirectory = (await DirectoryUtils.getDirectoryForInit()).path;
|
||||
final tempDirectory = io.Directory(_tempDirectory);
|
||||
try {
|
||||
final currentTime = DateTime.now().microsecondsSinceEpoch;
|
||||
if (tempDirectory.existsSync() &&
|
||||
(_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) <
|
||||
(currentTime - kTempFolderDeletionTimeBuffer)) {
|
||||
await tempDirectory.delete(recursive: true);
|
||||
await _preferences.setInt(lastTempFolderClearTimeKey, currentTime);
|
||||
_logger.info("Cleared temp folder");
|
||||
} else {
|
||||
_logger.info("Skipping temp folder clear");
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning(e);
|
||||
}
|
||||
tempDirectory.createSync(recursive: true);
|
||||
await _initOnlineAccount();
|
||||
sqfliteFfiInit();
|
||||
await _initOfflineAccount();
|
||||
}
|
||||
|
||||
@@ -99,303 +40,10 @@ class Configuration {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _initOnlineAccount() async {
|
||||
if (!_preferences.containsKey(tokenKey)) {
|
||||
for (final key in onlineSecureKeys) {
|
||||
unawaited(
|
||||
_secureStorage.delete(
|
||||
key: key,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_key = await _secureStorage.read(
|
||||
key: keyKey,
|
||||
);
|
||||
_secretKey = await _secureStorage.read(
|
||||
key: secretKeyKey,
|
||||
);
|
||||
_authSecretKey = await _secureStorage.read(
|
||||
key: authSecretKeyKey,
|
||||
);
|
||||
if (_key == null) {
|
||||
await logout(autoLogout: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> logout({bool autoLogout = false}) async {
|
||||
await _preferences.clear();
|
||||
for (String key in onlineSecureKeys) {
|
||||
await _secureStorage.delete(
|
||||
key: key,
|
||||
);
|
||||
}
|
||||
await AuthenticatorDB.instance.clearTable();
|
||||
_key = null;
|
||||
_cachedToken = null;
|
||||
_secretKey = null;
|
||||
_authSecretKey = null;
|
||||
Bus.instance.fire(SignedOutEvent());
|
||||
}
|
||||
|
||||
Future<KeyGenResult> generateKey(String password) async {
|
||||
// Create a master key
|
||||
final masterKey = CryptoUtil.generateKey();
|
||||
|
||||
// Create a recovery key
|
||||
final recoveryKey = CryptoUtil.generateKey();
|
||||
|
||||
// Encrypt master key and recovery key with each other
|
||||
final encryptedMasterKey = CryptoUtil.encryptSync(masterKey, recoveryKey);
|
||||
final encryptedRecoveryKey = CryptoUtil.encryptSync(recoveryKey, masterKey);
|
||||
|
||||
// Derive a key from the password that will be used to encrypt and
|
||||
// decrypt the master key
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||
utf8.encode(password),
|
||||
kekSalt,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||
|
||||
// Encrypt the key with this derived key
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(masterKey, derivedKeyResult.key);
|
||||
|
||||
// Generate a public-private keypair and encrypt the latter
|
||||
final keyPair = CryptoUtil.generateKeyPair();
|
||||
final encryptedSecretKeyData =
|
||||
CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey);
|
||||
|
||||
final attributes = KeyAttributes(
|
||||
CryptoUtil.bin2base64(kekSalt),
|
||||
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
||||
CryptoUtil.bin2base64(encryptedKeyData.nonce!),
|
||||
CryptoUtil.bin2base64(keyPair.publicKey),
|
||||
CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!),
|
||||
CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!),
|
||||
derivedKeyResult.memLimit,
|
||||
derivedKeyResult.opsLimit,
|
||||
CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!),
|
||||
CryptoUtil.bin2base64(encryptedMasterKey.nonce!),
|
||||
CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!),
|
||||
CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!),
|
||||
);
|
||||
final privateAttributes = PrivateKeyAttributes(
|
||||
CryptoUtil.bin2base64(masterKey),
|
||||
CryptoUtil.bin2hex(recoveryKey),
|
||||
CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()),
|
||||
);
|
||||
return KeyGenResult(attributes, privateAttributes, loginKey);
|
||||
}
|
||||
|
||||
Future<Tuple2<KeyAttributes, Uint8List>> getAttributesForNewPassword(
|
||||
String password,
|
||||
) async {
|
||||
// Get master key
|
||||
final masterKey = getKey();
|
||||
|
||||
// Derive a key from the password that will be used to encrypt and
|
||||
// decrypt the master key
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||
utf8.encode(password),
|
||||
kekSalt,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||
|
||||
// Encrypt the key with this derived key
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(masterKey!, derivedKeyResult.key);
|
||||
|
||||
final existingAttributes = getKeyAttributes();
|
||||
|
||||
final updatedAttributes = existingAttributes!.copyWith(
|
||||
kekSalt: CryptoUtil.bin2base64(kekSalt),
|
||||
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
||||
keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!),
|
||||
memLimit: derivedKeyResult.memLimit,
|
||||
opsLimit: derivedKeyResult.opsLimit,
|
||||
);
|
||||
return Tuple2(updatedAttributes, loginKey);
|
||||
}
|
||||
|
||||
// decryptSecretsAndGetLoginKey decrypts the master key and recovery key
|
||||
// with the given password and save them in local secure storage.
|
||||
// This method also returns the keyEncKey that can be used for performing
|
||||
// SRP setup for existing users.
|
||||
Future<Uint8List> decryptSecretsAndGetKeyEncKey(
|
||||
String password,
|
||||
KeyAttributes attributes, {
|
||||
Uint8List? keyEncryptionKey,
|
||||
}) async {
|
||||
_logger.info('Start decryptAndSaveSecrets');
|
||||
keyEncryptionKey ??= await CryptoUtil.deriveKey(
|
||||
utf8.encode(password),
|
||||
CryptoUtil.base642bin(attributes.kekSalt),
|
||||
attributes.memLimit,
|
||||
attributes.opsLimit,
|
||||
);
|
||||
|
||||
_logger.info('user-key done');
|
||||
Uint8List key;
|
||||
try {
|
||||
key = CryptoUtil.decryptSync(
|
||||
CryptoUtil.base642bin(attributes.encryptedKey),
|
||||
keyEncryptionKey,
|
||||
CryptoUtil.base642bin(attributes.keyDecryptionNonce),
|
||||
);
|
||||
} catch (e) {
|
||||
_logger.severe('master-key failed, incorrect password?', e);
|
||||
throw Exception("Incorrect password");
|
||||
}
|
||||
_logger.info("master-key done");
|
||||
await setKey(CryptoUtil.bin2base64(key));
|
||||
final secretKey = CryptoUtil.decryptSync(
|
||||
CryptoUtil.base642bin(attributes.encryptedSecretKey),
|
||||
key,
|
||||
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
|
||||
);
|
||||
_logger.info("secret-key done");
|
||||
await setSecretKey(CryptoUtil.bin2base64(secretKey));
|
||||
final token = CryptoUtil.openSealSync(
|
||||
CryptoUtil.base642bin(getEncryptedToken()!),
|
||||
CryptoUtil.base642bin(attributes.publicKey),
|
||||
secretKey,
|
||||
);
|
||||
_logger.info('appToken done');
|
||||
await setToken(
|
||||
CryptoUtil.bin2base64(token, urlSafe: true),
|
||||
);
|
||||
return keyEncryptionKey;
|
||||
}
|
||||
|
||||
Future<void> recover(String recoveryKey) async {
|
||||
// check if user has entered mnemonic code
|
||||
if (recoveryKey.contains(' ')) {
|
||||
final split = recoveryKey.split(' ');
|
||||
if (split.length != mnemonicKeyWordCount) {
|
||||
String wordThatIsFollowedByEmptySpaceInSplit = '';
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
String word = split[i];
|
||||
if (word.isEmpty) {
|
||||
wordThatIsFollowedByEmptySpaceInSplit =
|
||||
'\n\nExtra space after word at position $i';
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw AssertionError(
|
||||
'\nRecovery code should have $mnemonicKeyWordCount words, '
|
||||
'found ${split.length} words instead.$wordThatIsFollowedByEmptySpaceInSplit',
|
||||
);
|
||||
}
|
||||
recoveryKey = bip39.mnemonicToEntropy(recoveryKey);
|
||||
}
|
||||
final attributes = getKeyAttributes();
|
||||
Uint8List masterKey;
|
||||
try {
|
||||
masterKey = await CryptoUtil.decrypt(
|
||||
CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey),
|
||||
CryptoUtil.hex2bin(recoveryKey),
|
||||
CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce),
|
||||
);
|
||||
} catch (e) {
|
||||
_logger.severe(e);
|
||||
rethrow;
|
||||
}
|
||||
await setKey(CryptoUtil.bin2base64(masterKey));
|
||||
final secretKey = CryptoUtil.decryptSync(
|
||||
CryptoUtil.base642bin(attributes.encryptedSecretKey),
|
||||
masterKey,
|
||||
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
|
||||
);
|
||||
await setSecretKey(CryptoUtil.bin2base64(secretKey));
|
||||
final token = CryptoUtil.openSealSync(
|
||||
CryptoUtil.base642bin(getEncryptedToken()!),
|
||||
CryptoUtil.base642bin(attributes.publicKey),
|
||||
secretKey,
|
||||
);
|
||||
await setToken(
|
||||
CryptoUtil.bin2base64(token, urlSafe: true),
|
||||
);
|
||||
}
|
||||
|
||||
String getHttpEndpoint() {
|
||||
return _preferences.getString(endPointKey) ?? endpoint;
|
||||
}
|
||||
|
||||
Future<void> setHttpEndpoint(String endpoint) async {
|
||||
await _preferences.setString(endPointKey, endpoint);
|
||||
Bus.instance.fire(EndpointUpdatedEvent());
|
||||
}
|
||||
|
||||
String? getToken() {
|
||||
_cachedToken ??= _preferences.getString(tokenKey);
|
||||
return _cachedToken;
|
||||
}
|
||||
|
||||
bool isLoggedIn() {
|
||||
return getToken() != null;
|
||||
}
|
||||
|
||||
Future<void> setToken(String token) async {
|
||||
_cachedToken = token;
|
||||
await _preferences.setString(tokenKey, token);
|
||||
Bus.instance.fire(SignedInEvent());
|
||||
}
|
||||
|
||||
Future<void> setEncryptedToken(String encryptedToken) async {
|
||||
await _preferences.setString(encryptedTokenKey, encryptedToken);
|
||||
}
|
||||
|
||||
String? getEncryptedToken() {
|
||||
return _preferences.getString(encryptedTokenKey);
|
||||
}
|
||||
|
||||
String? getEmail() {
|
||||
return _preferences.getString(emailKey);
|
||||
}
|
||||
|
||||
Future<void> setEmail(String email) async {
|
||||
await _preferences.setString(emailKey, email);
|
||||
}
|
||||
|
||||
int? getUserID() {
|
||||
return _preferences.getInt(userIDKey);
|
||||
}
|
||||
|
||||
Future<void> setUserID(int userID) async {
|
||||
await _preferences.setInt(userIDKey, userID);
|
||||
}
|
||||
|
||||
Future<void> setKeyAttributes(KeyAttributes attributes) async {
|
||||
await _preferences.setString(keyAttributesKey, attributes.toJson());
|
||||
}
|
||||
|
||||
KeyAttributes? getKeyAttributes() {
|
||||
final jsonValue = _preferences.getString(keyAttributesKey);
|
||||
if (jsonValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
return KeyAttributes.fromJson(jsonValue);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setKey(String key) async {
|
||||
_key = key;
|
||||
await _secureStorage.write(
|
||||
key: keyKey,
|
||||
value: key,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setSecretKey(String? secretKey) async {
|
||||
_secretKey = secretKey;
|
||||
await _secureStorage.write(
|
||||
key: secretKeyKey,
|
||||
value: secretKey,
|
||||
);
|
||||
await super.logout();
|
||||
}
|
||||
|
||||
Future<void> setAuthSecretKey(String? authSecretKey) async {
|
||||
@@ -406,14 +54,6 @@ class Configuration {
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List? getKey() {
|
||||
return _key == null ? null : CryptoUtil.base642bin(_key!);
|
||||
}
|
||||
|
||||
Uint8List? getSecretKey() {
|
||||
return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!);
|
||||
}
|
||||
|
||||
Uint8List? getAuthSecretKey() {
|
||||
return _authSecretKey == null
|
||||
? null
|
||||
@@ -426,24 +66,6 @@ class Configuration {
|
||||
: CryptoUtil.base642bin(_offlineAuthKey!);
|
||||
}
|
||||
|
||||
Uint8List getRecoveryKey() {
|
||||
final keyAttributes = getKeyAttributes()!;
|
||||
return CryptoUtil.decryptSync(
|
||||
CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey),
|
||||
getKey()!,
|
||||
CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce),
|
||||
);
|
||||
}
|
||||
|
||||
// Caution: This directory is cleared on app start
|
||||
String getTempDirectory() {
|
||||
return _tempDirectory;
|
||||
}
|
||||
|
||||
bool hasConfiguredAccount() {
|
||||
return getToken() != null && _key != null;
|
||||
}
|
||||
|
||||
bool hasOptedForOfflineMode() {
|
||||
return _preferences.getBool(hasOptedForOfflineModeKey) ?? false;
|
||||
}
|
||||
@@ -464,16 +86,4 @@ class Configuration {
|
||||
}
|
||||
await _preferences.setBool(hasOptedForOfflineModeKey, true);
|
||||
}
|
||||
|
||||
void setVolatilePassword(String volatilePassword) {
|
||||
_volatilePassword = volatilePassword;
|
||||
}
|
||||
|
||||
void resetVolatilePassword() {
|
||||
_volatilePassword = null;
|
||||
}
|
||||
|
||||
String? getVolatilePassword() {
|
||||
return _volatilePassword;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
import 'package:ente_events/models/event.dart';
|
||||
|
||||
class CodesUpdatedEvent extends Event {}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
class EndpointUpdatedEvent extends Event {}
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
import 'package:ente_events/models/event.dart';
|
||||
|
||||
class IconsChangedEvent extends Event {}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
// NotificationEvent event is used to re-fresh the UI to show latest notification
|
||||
// (if any)
|
||||
class NotificationEvent extends Event {}
|
||||
@@ -1,3 +0,0 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
class SignedInEvent extends Event {}
|
||||
@@ -1,3 +1 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
class SignedOutEvent extends Event {}
|
||||
// TODO Implement this library.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
import 'package:ente_events/models/event.dart';
|
||||
|
||||
class TriggerLogoutEvent extends Event {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_auth/core/errors.dart';
|
||||
import 'package:ente_auth/core/network.dart';
|
||||
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||
import 'package:ente_auth/models/authenticator/auth_key.dart';
|
||||
import 'package:ente_network/network.dart';
|
||||
|
||||
class AuthenticatorGateway {
|
||||
late Dio _enteDio;
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
"useRecoveryKey": "Χρήση κλειδιού ανάκτησης",
|
||||
"incorrectPasswordTitle": "Λάθος κωδικός πρόσβασης",
|
||||
"welcomeBack": "Καλωσορίσατε και πάλι!",
|
||||
"emailAlreadyRegistered": "Το email είναι ήδη καταχωρημένο.",
|
||||
"emailNotRegistered": "Το email δεν έχει καταχωρηθεί.",
|
||||
"madeWithLoveAtPrefix": "φτιαγμένη με ❤️ στο ",
|
||||
"supportDevs": "Εγγραφείτε στο <bold-green>ente</bold-green> για να μας υποστηρίξετε",
|
||||
"supportDiscount": "Χρησιμοποιήστε τον κωδικό κουπονιού \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο",
|
||||
@@ -171,6 +173,7 @@
|
||||
"invalidQRCode": "Μη έγκυρος κωδικός QR",
|
||||
"noRecoveryKeyTitle": "Χωρίς κλειδί ανάκτησης;",
|
||||
"enterEmailHint": "Εισάγετε τη διεύθυνση email σας",
|
||||
"enterNewEmailHint": "Εισάγετε την διεύθυνση ηλ. ταχυδρομείου σας",
|
||||
"invalidEmailTitle": "Μη έγκυρη διεύθυνση email",
|
||||
"invalidEmailMessage": "Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.",
|
||||
"deleteAccount": "Διαγραφή λογαριασμού",
|
||||
@@ -258,6 +261,10 @@
|
||||
"areYouSureYouWantToLogout": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;",
|
||||
"yesLogout": "Ναι, αποσύνδεση",
|
||||
"exit": "Εξοδος",
|
||||
"theme": "Θέμα",
|
||||
"lightTheme": "Φωτεινό",
|
||||
"darkTheme": "Σκοτεινό",
|
||||
"systemTheme": "Σύστημα",
|
||||
"verifyingRecoveryKey": "Επαλήθευση κλειδιού ανάκτησης...",
|
||||
"recoveryKeyVerified": "Το κλειδί ανάκτησης επαληθεύτηκε",
|
||||
"recoveryKeySuccessBody": "Τέλεια! Το κλειδί ανάκτησης σας είναι έγκυρο. Σας ευχαριστούμε για την επαλήθευση.\n\nΠαρακαλώ θυμηθείτε να κρατήσετε το κλειδί ανάκτησης σας και σε αντίγραφο ασφαλείας.",
|
||||
@@ -490,5 +497,24 @@
|
||||
"appLockNotEnabled": "Το κλείδωμα εφαρμογής δεν είναι ενεργοποιημένο",
|
||||
"appLockNotEnabledDescription": "Παρακαλώ ενεργοποιήστε το κλείδωμα εφαρμογής μέσω της επιλογής Ασφάλεια > Κλείδωμα εφαρμογής",
|
||||
"authToViewPasskey": "Παρακαλώ πιστοποιηθείτε για να δείτε το κλειδί πρόσβασης",
|
||||
"appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας."
|
||||
"appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας.",
|
||||
"duplicateCodes": "Διπλότυποι κωδικοί",
|
||||
"noDuplicates": "✨ Δεν υπάρχουν διπλότυπα",
|
||||
"youveNoDuplicateCodesThatCanBeCleared": "Δεν υπάρχουν διπλότυπα αρχεία που μπορούν να εκκαθαριστούν",
|
||||
"deduplicateCodes": "Διπλότυποι κωδικοί",
|
||||
"deselectAll": "Αποεπιλογή όλων",
|
||||
"selectAll": "Επιλογή όλων",
|
||||
"deleteDuplicates": "Διαγραφή διπλότυπων",
|
||||
"plainHTML": "Απλό HTML",
|
||||
"dropReviewiOS": "Αφήστε μια κριτική στο App Store",
|
||||
"dropReviewAndroid": "Αφήστε μια κριτική στο Play Store",
|
||||
"giveUsAStarOnGithub": "Δώστε μας ένα αστέρι στο Github",
|
||||
"free5GB": "5GB δωρεάν στο <bold-green>ente</bold-green> Photos",
|
||||
"freeStorageOffer": "10% έκπτωση στο <bold-green>ente</bold-green> photos",
|
||||
"freeStorageOfferDescription": "Χρησιμοποιήστε τον κωδικό \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο",
|
||||
"advanced": "Για προχωρημένους",
|
||||
"algorithm": "Αλγόριθμος",
|
||||
"type": "Τύπος",
|
||||
"period": "Περίοδος",
|
||||
"digits": "Ψηφία"
|
||||
}
|
||||
@@ -111,6 +111,7 @@
|
||||
"importAegisGuide": "Use the \"Export the vault\" option in Aegis's Settings.\n\nIf your vault is encrypted, you will need to enter vault password to decrypt the vault.",
|
||||
"import2FasGuide": "Use the \"Settings->Backup -Export\" option in 2FAS.\n\nIf your backup is encrypted, you will need to enter the password to decrypt the backup",
|
||||
"importLastpassGuide": "Use the \"Transfer accounts\" option within Lastpass Authenticator Settings and press \"Export accounts to file\". Import the JSON downloaded.",
|
||||
"importProtonAuthGuide": "Use the \"Export\" option in Proton Authenticator Settings to export your codes.",
|
||||
"exportCodes": "Export codes",
|
||||
"importLabel": "Import",
|
||||
"importInstruction": "Please select a file that contains a list of your codes in the following format",
|
||||
@@ -519,5 +520,12 @@
|
||||
"algorithm": "Algorithm",
|
||||
"type": "Type",
|
||||
"period": "Period",
|
||||
"digits": "Digits"
|
||||
"digits": "Digits",
|
||||
"importFromGallery": "Import from gallery",
|
||||
"errorCouldNotReadImage": "Could not read the selected image file.",
|
||||
"errorInvalidQRCode": "Invalid QR Code",
|
||||
"errorInvalidQRCodeBody": "The scanned QR code is not a valid 2FA account.",
|
||||
"errorNoQRCode": "No QR code found",
|
||||
"errorGenericTitle": "An Error Occurred",
|
||||
"errorGenericBody": "An unexpected error occurred while importing."
|
||||
}
|
||||
@@ -45,7 +45,7 @@
|
||||
"timeBasedKeyType": "Oparte na czasie (TOTP)",
|
||||
"counterBasedKeyType": "Oparte na liczniku (HOTP)",
|
||||
"saveAction": "Zapisz",
|
||||
"nextTotpTitle": "następny",
|
||||
"nextTotpTitle": "dalej",
|
||||
"deleteCodeTitle": "Usunąć kod?",
|
||||
"deleteCodeMessage": "Czy na pewno chcesz usunąć ten kod? Ta akcja jest nieodwracalna.",
|
||||
"trashCode": "Przenieść kod do kosza?",
|
||||
|
||||
@@ -6,4 +6,4 @@ export "package:ente_auth/l10n/arb/app_localizations.dart"
|
||||
|
||||
extension AppLocalizationsX on BuildContext {
|
||||
AppLocalizations get l10n => AppLocalizations.of(this);
|
||||
}
|
||||
}
|
||||
@@ -2,34 +2,38 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
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/l10n/l10n.dart';
|
||||
import 'package:ente_auth/locale.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/preference_service.dart';
|
||||
import 'package:ente_auth/services/update_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/services/window_listener_service.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/store/code_display_store.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||
import 'package:ente_auth/ui/tools/lock_screen.dart';
|
||||
import 'package:ente_auth/ui/home_page.dart';
|
||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||
import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/window_protocol_handler.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:ente_lock_screen/lock_screen_settings.dart';
|
||||
import 'package:ente_lock_screen/ui/app_lock.dart';
|
||||
import 'package:ente_lock_screen/ui/lock_screen.dart';
|
||||
import 'package:ente_logging/logging.dart';
|
||||
import 'package:ente_network/network.dart';
|
||||
import 'package:ente_strings/l10n/strings_localizations.dart';
|
||||
import 'package:ente_ui/theme/theme_config.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
@@ -89,7 +93,9 @@ void main() async {
|
||||
}
|
||||
|
||||
Future<void> _runInForeground() async {
|
||||
AppThemeConfig.initialize(EnteApp.auth);
|
||||
final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode());
|
||||
final configuration = Configuration.instance;
|
||||
return await _runWithLogs(() async {
|
||||
_logger.info("Starting app in foreground");
|
||||
try {
|
||||
@@ -103,12 +109,19 @@ Future<void> _runInForeground() async {
|
||||
runApp(
|
||||
AppLock(
|
||||
builder: (args) => App(locale: locale),
|
||||
lockScreen: const LockScreen(),
|
||||
lockScreen: LockScreen(configuration),
|
||||
enabled: await LockScreenSettings.instance.shouldShowLockScreen(),
|
||||
locale: locale,
|
||||
lightTheme: lightThemeData,
|
||||
darkTheme: darkThemeData,
|
||||
savedThemeMode: savedThemeMode,
|
||||
localeListResolutionCallback: localResolutionCallBack,
|
||||
localizationsDelegates: const [
|
||||
...StringsLocalizations.localizationsDelegates,
|
||||
...AppLocalizations.localizationsDelegates,
|
||||
],
|
||||
supportedLocales: appSupportedLocales,
|
||||
backgroundLockLatency: const Duration(seconds: 0),
|
||||
),
|
||||
);
|
||||
});
|
||||
@@ -154,13 +167,13 @@ Future<void> _init(bool bool, {String? via}) async {
|
||||
await PreferenceService.instance.init();
|
||||
await CodeStore.instance.init();
|
||||
await CodeDisplayStore.instance.init();
|
||||
await Configuration.instance.init();
|
||||
await Network.instance.init();
|
||||
await UserService.instance.init();
|
||||
await Configuration.instance.init([AuthenticatorDB.instance]);
|
||||
await Network.instance.init(Configuration.instance);
|
||||
await UserService.instance.init(Configuration.instance, const HomePage());
|
||||
await AuthenticatorService.instance.init();
|
||||
await BillingService.instance.init();
|
||||
await NotificationService.instance.init();
|
||||
await UpdateService.instance.init();
|
||||
await IconUtils.instance.init();
|
||||
await LockScreenSettings.instance.init();
|
||||
await LockScreenSettings.instance.init(Configuration.instance);
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
const freeProductID = "free";
|
||||
const stripe = "stripe";
|
||||
const appStore = "appstore";
|
||||
const playStore = "playstore";
|
||||
|
||||
class Subscription {
|
||||
final String productID;
|
||||
final int storage;
|
||||
final String originalTransactionID;
|
||||
final String paymentProvider;
|
||||
final int expiryTime;
|
||||
final String price;
|
||||
final String period;
|
||||
final Attributes? attributes;
|
||||
|
||||
Subscription({
|
||||
required this.productID,
|
||||
required this.storage,
|
||||
required this.originalTransactionID,
|
||||
required this.paymentProvider,
|
||||
required this.expiryTime,
|
||||
required this.price,
|
||||
required this.period,
|
||||
this.attributes,
|
||||
});
|
||||
|
||||
bool isValid() {
|
||||
return expiryTime > DateTime.now().microsecondsSinceEpoch;
|
||||
}
|
||||
|
||||
bool isYearlyPlan() {
|
||||
return 'year' == period;
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
return Subscription(
|
||||
productID: map['productID'],
|
||||
storage: map['storage'],
|
||||
originalTransactionID: map['originalTransactionID'],
|
||||
paymentProvider: map['paymentProvider'],
|
||||
expiryTime: map['expiryTime'],
|
||||
price: map['price'],
|
||||
period: map['period'],
|
||||
attributes: map["attributes"] != null
|
||||
? Attributes.fromJson(map["attributes"])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Attributes {
|
||||
bool? isCancelled;
|
||||
String? customerID;
|
||||
|
||||
Attributes({
|
||||
this.isCancelled,
|
||||
this.customerID,
|
||||
});
|
||||
|
||||
Attributes.fromJson(dynamic json) {
|
||||
isCancelled = json["isCancelled"];
|
||||
customerID = json["customerID"];
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ente_auth/models/subscription.dart';
|
||||
|
||||
class UserDetails {
|
||||
final String email;
|
||||
final int usage;
|
||||
final int fileCount;
|
||||
final int sharedCollectionsCount;
|
||||
final Subscription subscription;
|
||||
final FamilyData? familyData;
|
||||
final ProfileData? profileData;
|
||||
|
||||
UserDetails(
|
||||
this.email,
|
||||
this.usage,
|
||||
this.fileCount,
|
||||
this.sharedCollectionsCount,
|
||||
this.subscription,
|
||||
this.familyData,
|
||||
this.profileData,
|
||||
);
|
||||
|
||||
bool isPartOfFamily() {
|
||||
return familyData?.members?.isNotEmpty ?? false;
|
||||
}
|
||||
|
||||
bool isFamilyAdmin() {
|
||||
assert(isPartOfFamily(), "verify user is part of family before calling");
|
||||
final FamilyMember currentUserMember = familyData!.members!
|
||||
.firstWhere((element) => element.email.trim() == email.trim());
|
||||
return currentUserMember.isAdmin;
|
||||
}
|
||||
|
||||
// getFamilyOrPersonalUsage will return total usage for family if user
|
||||
// belong to family group. Otherwise, it will return storage consumed by
|
||||
// current user
|
||||
int getFamilyOrPersonalUsage() {
|
||||
return isPartOfFamily() ? familyData!.getTotalUsage() : usage;
|
||||
}
|
||||
|
||||
int getFreeStorage() {
|
||||
return max(
|
||||
isPartOfFamily()
|
||||
? (familyData!.storage - familyData!.getTotalUsage())
|
||||
: (subscription.storage - (usage)),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
int getTotalStorage() {
|
||||
return isPartOfFamily() ? familyData!.storage : subscription.storage;
|
||||
}
|
||||
|
||||
factory UserDetails.fromMap(Map<String, dynamic> map) {
|
||||
return UserDetails(
|
||||
map['email'] as String,
|
||||
map['usage'] as int,
|
||||
(map['fileCount'] ?? 0) as int,
|
||||
(map['sharedCollectionsCount'] ?? 0) as int,
|
||||
Subscription.fromMap(map['subscription']),
|
||||
FamilyData.fromMap(map['familyData']),
|
||||
ProfileData.fromJson(map['profileData']),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FamilyMember {
|
||||
final String email;
|
||||
final int usage;
|
||||
final String id;
|
||||
final bool isAdmin;
|
||||
|
||||
FamilyMember(this.email, this.usage, this.id, this.isAdmin);
|
||||
|
||||
factory FamilyMember.fromMap(Map<String, dynamic> map) {
|
||||
return FamilyMember(
|
||||
(map['email'] ?? '') as String,
|
||||
map['usage'] as int,
|
||||
map['id'] as String,
|
||||
map['isAdmin'] as bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
class ProfileData {
|
||||
bool canDisableEmailMFA;
|
||||
bool isEmailMFAEnabled;
|
||||
bool isTwoFactorEnabled;
|
||||
|
||||
// Constructor with default values
|
||||
ProfileData({
|
||||
this.canDisableEmailMFA = false,
|
||||
this.isEmailMFAEnabled = false,
|
||||
this.isTwoFactorEnabled = false,
|
||||
});
|
||||
|
||||
// Factory method to create ProfileData instance from JSON
|
||||
factory ProfileData.fromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) null;
|
||||
|
||||
return ProfileData(
|
||||
canDisableEmailMFA: json!['canDisableEmailMFA'] ?? false,
|
||||
isEmailMFAEnabled: json['isEmailMFAEnabled'] ?? false,
|
||||
isTwoFactorEnabled: json['isTwoFactorEnabled'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
// Method to convert ProfileData instance to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'canDisableEmailMFA': canDisableEmailMFA,
|
||||
'isEmailMFAEnabled': isEmailMFAEnabled,
|
||||
'isTwoFactorEnabled': isTwoFactorEnabled,
|
||||
};
|
||||
}
|
||||
String toJsonString() => json.encode(toJson());
|
||||
}
|
||||
class FamilyData {
|
||||
final List<FamilyMember>? members;
|
||||
|
||||
// Storage available based on the family plan
|
||||
final int storage;
|
||||
final int expiryTime;
|
||||
|
||||
FamilyData(this.members, this.storage, this.expiryTime);
|
||||
|
||||
int getTotalUsage() {
|
||||
return members!.map((e) => e.usage).toList().sum;
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
assert(map['members'] != null && map['members'].length >= 0);
|
||||
final members = List<FamilyMember>.from(
|
||||
map['members'].map((x) => FamilyMember.fromMap(x)),
|
||||
);
|
||||
return FamilyData(
|
||||
members,
|
||||
map['storage'] as int,
|
||||
map['expiryTime'] as int,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_accounts/pages/email_entry_page.dart';
|
||||
import 'package:ente_accounts/pages/login_page.dart';
|
||||
import 'package:ente_accounts/pages/password_entry_page.dart';
|
||||
import 'package:ente_accounts/pages/password_reentry_page.dart';
|
||||
import 'package:ente_auth/app/view/app.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/events/trigger_logout_event.dart';
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import 'package:ente_auth/locale.dart';
|
||||
import 'package:ente_auth/theme/text_style.dart';
|
||||
import 'package:ente_auth/ui/account/email_entry_page.dart';
|
||||
import 'package:ente_auth/ui/account/login_page.dart';
|
||||
import 'package:ente_auth/ui/account/logout_dialog.dart';
|
||||
import 'package:ente_auth/ui/account/password_entry_page.dart';
|
||||
import 'package:ente_auth/ui/account/password_reentry_page.dart';
|
||||
import 'package:ente_auth/ui/common/gradient_button.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
@@ -24,6 +23,7 @@ import 'package:ente_auth/ui/settings/language_picker.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
@@ -260,17 +260,22 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
void _navigateToSignUpPage() {
|
||||
Widget page;
|
||||
if (Configuration.instance.getEncryptedToken() == null) {
|
||||
page = const EmailEntryPage();
|
||||
page = EmailEntryPage(Configuration.instance);
|
||||
} else {
|
||||
// No key
|
||||
if (Configuration.instance.getKeyAttributes() == null) {
|
||||
// Never had a key
|
||||
page = const PasswordEntryPage(
|
||||
mode: PasswordEntryMode.set,
|
||||
page = PasswordEntryPage(
|
||||
Configuration.instance,
|
||||
PasswordEntryMode.set,
|
||||
const HomePage(),
|
||||
);
|
||||
} else if (Configuration.instance.getKey() == null) {
|
||||
// Yet to decrypt the key
|
||||
page = const PasswordReentryPage();
|
||||
page = PasswordReentryPage(
|
||||
Configuration.instance,
|
||||
const HomePage(),
|
||||
);
|
||||
} else {
|
||||
// All is well, user just has not subscribed
|
||||
page = const HomePage();
|
||||
@@ -288,17 +293,22 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
void _navigateToSignInPage() {
|
||||
Widget page;
|
||||
if (Configuration.instance.getEncryptedToken() == null) {
|
||||
page = const LoginPage();
|
||||
page = LoginPage(Configuration.instance);
|
||||
} else {
|
||||
// No key
|
||||
if (Configuration.instance.getKeyAttributes() == null) {
|
||||
// Never had a key
|
||||
page = const PasswordEntryPage(
|
||||
mode: PasswordEntryMode.set,
|
||||
page = PasswordEntryPage(
|
||||
Configuration.instance,
|
||||
PasswordEntryMode.set,
|
||||
const HomePage(),
|
||||
);
|
||||
} else if (Configuration.instance.getKey() == null) {
|
||||
// Yet to decrypt the key
|
||||
page = const PasswordReentryPage();
|
||||
page = PasswordReentryPage(
|
||||
Configuration.instance,
|
||||
const HomePage(),
|
||||
);
|
||||
} else {
|
||||
// All is well, user just has not subscribed
|
||||
// page = getSubscriptionPage(isOnBoarding: true);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import "package:ente_auth/l10n/l10n.dart";
|
||||
import 'package:ente_auth/models/all_icon_data.dart';
|
||||
@@ -23,6 +22,7 @@ import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/errors.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/events/signed_in_event.dart';
|
||||
import 'package:ente_auth/events/trigger_logout_event.dart';
|
||||
import 'package:ente_auth/gateway/authenticator.dart';
|
||||
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||
@@ -17,6 +15,8 @@ import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:ente_events/models/signed_in_event.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_accounts/ente_accounts.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/errors.dart';
|
||||
import 'package:ente_auth/core/network.dart';
|
||||
import 'package:ente_auth/models/billing_plan.dart';
|
||||
import 'package:ente_auth/models/subscription.dart';
|
||||
import 'package:ente_auth/models/billing_plan.dart';
|
||||
import 'package:ente_network/network.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
const kWebPaymentRedirectUrl = "https://payments.ente.io/frameRedirect";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/icons_changed_event.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
enum CodeSortKey {
|
||||
|
||||
@@ -2,9 +2,9 @@ import 'dart:async';
|
||||
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:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_network/network.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -4,13 +4,14 @@ import 'dart:io';
|
||||
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||
import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_base/ente_base.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
class AuthenticatorDB {
|
||||
class AuthenticatorDB extends EnteBaseDatabase {
|
||||
static const _databaseName = "ente.authenticator.db";
|
||||
static const _databaseVersion = 1;
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/models/authenticator/entity_result.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/authenticator_service.dart';
|
||||
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class CodeStore {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart';
|
||||
import 'package:ente_auth/onboarding/view/view_qr_page.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
@@ -22,6 +21,7 @@ import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_context_menu/flutter_context_menu.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum DialogUserChoice { firstChoice, secondChoice }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/theme/text_style.dart';
|
||||
@@ -9,6 +8,7 @@ import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/components/models/custom_button_style.dart';
|
||||
import 'package:ente_auth/utils/debouncer.dart';
|
||||
import "package:ente_auth/utils/dialog_util.dart";
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/theme/effects.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
@@ -11,6 +10,7 @@ import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/components/separators.dart';
|
||||
import 'package:ente_auth/ui/components/text_input_widget.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///Will return null if dismissed by tapping outside
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/components/menu_item_child_widgets.dart';
|
||||
import 'package:ente_auth/utils/debouncer.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:ente_auth/ui/components/separators.dart';
|
||||
import 'package:ente_auth/utils/debouncer.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/models/execution_states.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:ente_auth/utils/debouncer.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef OnChangedCallBack = void Function(bool);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CoachMarkWidget extends StatelessWidget {
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'dart:io';
|
||||
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:ente_auth/events/codes_updated_event.dart';
|
||||
import 'package:ente_auth/events/icons_changed_event.dart';
|
||||
@@ -15,7 +15,6 @@ import 'package:ente_auth/onboarding/model/tag_enums.dart';
|
||||
import 'package:ente_auth/onboarding/view/common/tag_chip.dart';
|
||||
import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/store/code_display_store.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
@@ -34,11 +33,15 @@ import 'package:ente_auth/ui/reorder_codes_page.dart';
|
||||
import 'package:ente_auth/ui/scanner_page.dart';
|
||||
import 'package:ente_auth/ui/settings_page.dart';
|
||||
import 'package:ente_auth/ui/sort_option_menu.dart';
|
||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/totp_util.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:ente_lock_screen/lock_screen_settings.dart';
|
||||
import 'package:ente_lock_screen/ui/app_lock.dart';
|
||||
import 'package:ente_qr/ente_qr.dart';
|
||||
import 'package:ente_ui/pages/base_home_page.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -47,7 +50,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:move_to_background/move_to_background.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
class HomePage extends BaseHomePage {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
@@ -61,6 +64,7 @@ class _HomePageState extends State<HomePage> {
|
||||
);
|
||||
bool _hasLoaded = false;
|
||||
bool _isSettingsOpen = false;
|
||||
bool _isImportingFromGallery = false;
|
||||
final Logger _logger = Logger("HomePage");
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
@@ -287,6 +291,63 @@ class _HomePageState extends State<HomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _importFromGalleryNative() async {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
if (_isImportingFromGallery) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isImportingFromGallery = true;
|
||||
|
||||
try {
|
||||
final FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
);
|
||||
|
||||
if (result == null || result.files.single.path == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String imagePath = result.files.single.path!;
|
||||
final enteQr = EnteQr();
|
||||
final QrScanResult qrResult = await enteQr.scanQrFromImage(imagePath);
|
||||
|
||||
if (qrResult.success && qrResult.content != null) {
|
||||
try {
|
||||
final newCode = Code.fromOTPAuthUrl(qrResult.content!);
|
||||
await CodeStore.instance.addCode(newCode, shouldSync: false);
|
||||
// Focus the new code by searching
|
||||
if ((_allCodes?.where((e) => !e.hasError).length ?? 0) > 2) {
|
||||
_focusNewCode(newCode);
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.severe('Error adding code from QR scan', e);
|
||||
await showErrorDialog(
|
||||
context,
|
||||
l10n.errorInvalidQRCode,
|
||||
l10n.errorInvalidQRCodeBody,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_logger.warning('QR scan failed: ${qrResult.error}');
|
||||
await showErrorDialog(
|
||||
context,
|
||||
l10n.errorNoQRCode,
|
||||
qrResult.error ?? l10n.errorNoQRCode,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await showErrorDialog(
|
||||
context,
|
||||
l10n.errorGenericTitle,
|
||||
l10n.errorGenericBody,
|
||||
);
|
||||
} finally {
|
||||
_isImportingFromGallery = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _redirectToScannerPage() async {
|
||||
final Code? code = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
@@ -744,6 +805,14 @@ class _HomePageState extends State<HomePage> {
|
||||
labelWidget: SpeedDialLabelWidget(context.l10n.enterDetailsManually),
|
||||
onTap: _redirectToManualEntryPage,
|
||||
),
|
||||
if (PlatformUtil.isMobile())
|
||||
SpeedDialChild(
|
||||
child: const Icon(Icons.image),
|
||||
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
|
||||
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
|
||||
labelWidget: SpeedDialLabelWidget(context.l10n.importFromGallery),
|
||||
onTap: _importFromGalleryNative,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||
|
||||
class ScannerPage extends StatefulWidget {
|
||||
const ScannerPage({super.key});
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import 'package:ente_accounts/pages/change_email_dialog.dart';
|
||||
import 'package:ente_accounts/pages/delete_account_page.dart';
|
||||
import 'package:ente_accounts/pages/password_entry_page.dart';
|
||||
import 'package:ente_accounts/pages/recovery_key_page.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/account/change_email_dialog.dart';
|
||||
import 'package:ente_auth/ui/account/delete_account_page.dart';
|
||||
import 'package:ente_auth/ui/account/password_entry_page.dart';
|
||||
import 'package:ente_auth/ui/account/recovery_key_page.dart';
|
||||
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
|
||||
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/components/menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/home_page.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AccountSectionWidget extends StatelessWidget {
|
||||
@@ -81,8 +82,10 @@ class AccountSectionWidget extends StatelessWidget {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const PasswordEntryPage(
|
||||
mode: PasswordEntryMode.update,
|
||||
return PasswordEntryPage(
|
||||
Configuration.instance,
|
||||
PasswordEntryMode.update,
|
||||
const HomePage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -121,6 +124,7 @@ class AccountSectionWidget extends StatelessWidget {
|
||||
routeToPage(
|
||||
context,
|
||||
RecoveryKeyPage(
|
||||
Configuration.instance,
|
||||
recoveryKey,
|
||||
l10n.ok,
|
||||
showAppBar: true,
|
||||
@@ -151,8 +155,9 @@ class AccountSectionWidget extends StatelessWidget {
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
final config = Configuration.instance;
|
||||
// ignore: unawaited_futures
|
||||
routeToPage(context, const DeleteAccountPage());
|
||||
routeToPage(context, DeleteAccountPage(config));
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/deduplication_service.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/code_widget.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/export/ente.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/models/export/ente.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
@@ -14,6 +13,7 @@ import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/share_utils.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:file_saver/file_saver.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:ente_auth/ui/settings/data/import/encrypted_ente_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/google_auth_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/lastpass_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/plain_text_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/proton_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/raivo_plain_text_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/two_fas_import.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import_page.dart';
|
||||
@@ -43,6 +44,9 @@ class ImportService {
|
||||
case ImportType.lastpass:
|
||||
await showLastpassImportInstruction(context);
|
||||
break;
|
||||
case ImportType.proton:
|
||||
await showProtonImportInstruction(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
171
mobile/apps/auth/lib/ui/settings/data/import/proton_import.dart
Normal file
171
mobile/apps/auth/lib/ui/settings/data/import/proton_import.dart
Normal file
@@ -0,0 +1,171 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/authenticator_service.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/ui/common/progress_dialog.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/ui/settings/data/import/import_success.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
Future<void> showProtonImportInstruction(BuildContext context) async {
|
||||
final l10n = context.l10n;
|
||||
final result = await showDialogWidget(
|
||||
context: context,
|
||||
title: l10n.importFromApp("Proton Authenticator"),
|
||||
body: l10n.importProtonAuthGuide,
|
||||
buttons: [
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.primary,
|
||||
labelText: l10n.importSelectJsonFile,
|
||||
isInAlert: true,
|
||||
buttonSize: ButtonSize.large,
|
||||
buttonAction: ButtonAction.first,
|
||||
),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.secondary,
|
||||
labelText: context.l10n.cancel,
|
||||
buttonSize: ButtonSize.large,
|
||||
isInAlert: true,
|
||||
buttonAction: ButtonAction.second,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (result?.action != null && result!.action != ButtonAction.cancel) {
|
||||
if (result.action == ButtonAction.first) {
|
||||
await _pickProtonJsonFile(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickProtonJsonFile(BuildContext context) async {
|
||||
final l10n = context.l10n;
|
||||
FilePickerResult? result = await FilePicker.platform
|
||||
.pickFiles(dialogTitle: l10n.importSelectJsonFile);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
final ProgressDialog progressDialog =
|
||||
createProgressDialog(context, l10n.pleaseWait);
|
||||
await progressDialog.show();
|
||||
try {
|
||||
String path = result.files.single.path!;
|
||||
int? count = await _processProtonExportFile(context, path, progressDialog);
|
||||
await progressDialog.hide();
|
||||
if (count != null) {
|
||||
await importSuccessDialog(context, count);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logger('ProtonImport')
|
||||
.severe('exception while processing proton import', e, s);
|
||||
await progressDialog.hide();
|
||||
await showErrorDialog(
|
||||
context,
|
||||
context.l10n.sorry,
|
||||
"${context.l10n.importFailureDescNew}\n Error: ${e.toString()}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<int?> _processProtonExportFile(
|
||||
BuildContext context,
|
||||
String path,
|
||||
final ProgressDialog dialog,
|
||||
) async {
|
||||
File file = File(path);
|
||||
|
||||
final jsonString = await file.readAsString();
|
||||
final decodedJson = jsonDecode(jsonString);
|
||||
|
||||
// Validate that this is a Proton export
|
||||
if (decodedJson['version'] == null || decodedJson['entries'] == null) {
|
||||
await dialog.hide();
|
||||
await showErrorDialog(
|
||||
context,
|
||||
'Invalid Proton export',
|
||||
'The selected file is not a valid Proton Authenticator export.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
final parsedCodes = <Code>[];
|
||||
final entries = decodedJson['entries'] as List;
|
||||
|
||||
for (var entry in entries) {
|
||||
try {
|
||||
final content = entry['content'];
|
||||
if (content == null) {
|
||||
continue; // Skip entries without content
|
||||
}
|
||||
|
||||
final entryType = content['entry_type'] as String?;
|
||||
if (entryType != 'Totp' && entryType != 'Steam') {
|
||||
// log warning
|
||||
Logger('ProtonImport').warning('Unsupported entry type: $entryType');
|
||||
continue; // Skip non-TOTP and non-Steam entries
|
||||
}
|
||||
|
||||
Code code;
|
||||
|
||||
if (entryType == 'Steam') {
|
||||
// Handle Steam entries with steam:// format
|
||||
final steamUri = content['uri'] as String?;
|
||||
if (steamUri == null || !steamUri.startsWith('steam://')) {
|
||||
continue; // Skip invalid Steam URIs
|
||||
}
|
||||
|
||||
final secret = steamUri.split('steam://')[1];
|
||||
final name = content['name'] as String? ?? '';
|
||||
|
||||
code = Code.fromAccountAndSecret(
|
||||
Type.steam,
|
||||
'', // Steam doesn't typically have separate account
|
||||
name, // Use name as issuer
|
||||
secret,
|
||||
null,
|
||||
Code.steamDigits,
|
||||
);
|
||||
} else {
|
||||
// Handle TOTP entries with otpauth:// format
|
||||
final otpUri = content['uri'] as String?;
|
||||
if (otpUri == null || !otpUri.startsWith('otpauth://')) {
|
||||
continue; // Skip invalid OTP URIs
|
||||
}
|
||||
// Create code from OTP auth URL
|
||||
code = Code.fromOTPAuthUrl(otpUri);
|
||||
}
|
||||
|
||||
// Add note if present
|
||||
final note = entry['note'] as String?;
|
||||
if (note != null && note.isNotEmpty) {
|
||||
code = code.copyWith(
|
||||
display: code.display.copyWith(note: note),
|
||||
);
|
||||
}
|
||||
|
||||
parsedCodes.add(code);
|
||||
} catch (e, s) {
|
||||
Logger('ProtonImport').warning('Failed to parse entry', e, s);
|
||||
// Continue processing other entries
|
||||
}
|
||||
}
|
||||
|
||||
// Add all parsed codes to the store
|
||||
for (final code in parsedCodes) {
|
||||
await CodeStore.instance.addCode(code, shouldSync: false);
|
||||
}
|
||||
|
||||
// Trigger sync
|
||||
unawaited(AuthenticatorService.instance.onlineSync());
|
||||
|
||||
return parsedCodes.length;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ enum ImportType {
|
||||
twoFas,
|
||||
bitwarden,
|
||||
lastpass,
|
||||
proton,
|
||||
}
|
||||
|
||||
class ImportCodePage extends StatelessWidget {
|
||||
@@ -29,6 +30,7 @@ class ImportCodePage extends StatelessWidget {
|
||||
ImportType.aegis,
|
||||
ImportType.bitwarden,
|
||||
ImportType.googleAuthenticator,
|
||||
ImportType.proton,
|
||||
ImportType.ravio,
|
||||
ImportType.lastpass,
|
||||
];
|
||||
@@ -51,6 +53,8 @@ class ImportCodePage extends StatelessWidget {
|
||||
return 'Bitwarden';
|
||||
case ImportType.lastpass:
|
||||
return 'LastPass Authenticator';
|
||||
case ImportType.proton:
|
||||
return 'Proton Authenticator';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_auth/app/view/app.dart';
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/core/logging/super_logging.dart';
|
||||
import 'package:ente_auth/events/icons_changed_event.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/locale.dart';
|
||||
@@ -16,6 +14,8 @@ import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/ui/settings/language_picker.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_events/event_bus.dart';
|
||||
import 'package:ente_logging/logging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AdvancedSectionWidget extends StatefulWidget {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_accounts/models/user_details.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/user_details.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/ui/components/banner_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ente_accounts/models/user_details.dart';
|
||||
import 'package:ente_accounts/pages/request_pwd_verification_page.dart';
|
||||
import 'package:ente_accounts/pages/sessions_page.dart';
|
||||
import 'package:ente_accounts/services/passkey_service.dart';
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/user_details.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/services/passkey_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:ente_auth/ui/account/request_pwd_verification_page.dart';
|
||||
import 'package:ente_auth/ui/account/sessions_page.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
|
||||
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
|
||||
@@ -17,14 +16,15 @@ import 'package:ente_auth/ui/components/menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/ui/components/toggle_switch_widget.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/ui/settings/lock_screen/lock_screen_options.dart';
|
||||
import 'package:ente_auth/utils/auth_util.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/lock_screen_settings.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:ente_lock_screen/auth_util.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:ente_lock_screen/lock_screen_settings.dart';
|
||||
import 'package:ente_lock_screen/ui/lock_screen_options.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
@@ -128,7 +128,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return const SessionsPage();
|
||||
return SessionsPage(Configuration.instance);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -243,6 +243,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||
await routeToPage(
|
||||
context,
|
||||
RequestPasswordVerificationPage(
|
||||
Configuration.instance,
|
||||
onPasswordVerified: (Uint8List keyEncryptionKey) async {
|
||||
final Uint8List loginKey =
|
||||
await CryptoUtil.deriveLoginKey(keyEncryptionKey);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ente_accounts/services/user_service.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/onboarding/view/onboarding_page.dart';
|
||||
import 'package:ente_auth/services/local_authentication_service.dart';
|
||||
import 'package:ente_auth/services/user_service.dart';
|
||||
import 'package:ente_auth/onboarding/view/onboarding_page.dart';
|
||||
import 'package:ente_auth/store/code_store.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
@@ -27,6 +26,7 @@ import 'package:ente_auth/ui/settings/title_bar_widget.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_lock_screen/local_authentication_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:confetti/confetti.dart';
|
||||
import "package:dio/dio.dart";
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/typedefs.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/theme/colors.dart';
|
||||
import 'package:ente_auth/ui/common/loading_widget.dart';
|
||||
import 'package:ente_auth/ui/common/progress_dialog.dart';
|
||||
@@ -16,6 +15,7 @@ import 'package:ente_auth/ui/components/models/button_result.dart';
|
||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||
import 'package:ente_auth/utils/email_util.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_base/typedefs.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:email_validator/email_validator.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/core/logging/super_logging.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||
import 'package:ente_auth/ui/components/dialog_widget.dart';
|
||||
@@ -14,11 +13,11 @@ import 'package:ente_auth/utils/directory_utils.dart';
|
||||
import 'package:ente_auth/utils/platform_util.dart';
|
||||
import 'package:ente_auth/utils/share_utils.dart';
|
||||
import 'package:ente_auth/utils/toast_util.dart';
|
||||
import 'package:ente_logging/logging.dart';
|
||||
import "package:file_saver/file_saver.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import "package:intl/intl.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
@@ -59,14 +59,14 @@ class PlatformUtil {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
await FileSaver.instance.saveAs(
|
||||
name: fileName,
|
||||
ext: extension,
|
||||
fileExtension: extension,
|
||||
bytes: bytes,
|
||||
mimeType: type,
|
||||
);
|
||||
} else {
|
||||
await FileSaver.instance.saveFile(
|
||||
name: fileName,
|
||||
ext: extension,
|
||||
fileExtension: extension,
|
||||
bytes: bytes,
|
||||
mimeType: type,
|
||||
);
|
||||
|
||||
33
mobile/apps/auth/plugins/qr/.metadata
Normal file
33
mobile/apps/auth/plugins/qr/.metadata
Normal file
@@ -0,0 +1,33 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
|
||||
channel: "stable"
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
- platform: android
|
||||
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
- platform: ios
|
||||
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
8
mobile/apps/auth/plugins/qr/CHANGELOG.md
Normal file
8
mobile/apps/auth/plugins/qr/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## 0.0.1
|
||||
|
||||
* Initial release of ente_qr plugin
|
||||
* Support for scanning QR codes from image files
|
||||
* Android implementation using ZXing library
|
||||
* iOS implementation using Core Image framework
|
||||
* Comprehensive error handling
|
||||
* Example app demonstrating usage with file picker
|
||||
170
mobile/apps/auth/plugins/qr/README.md
Normal file
170
mobile/apps/auth/plugins/qr/README.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# ente_qr
|
||||
|
||||
A Flutter plugin for scanning QR codes from image files. This plugin provides a simple interface to scan QR codes from images on both Android and iOS platforms.
|
||||
|
||||
## Features
|
||||
|
||||
- Scan QR codes from image files
|
||||
- Support for Android (using ZXing library)
|
||||
- Support for iOS (using AVFoundation/Core Image)
|
||||
- Returns structured results with error handling
|
||||
- Works with images from file picker or camera
|
||||
|
||||
## Platform Support
|
||||
|
||||
| Platform | Support |
|
||||
|----------|---------|
|
||||
| Android | ✅ |
|
||||
| iOS | ✅ |
|
||||
| Web | ❌ |
|
||||
| macOS | ❌ |
|
||||
| Windows | ❌ |
|
||||
| Linux | ❌ |
|
||||
|
||||
## Installation
|
||||
|
||||
Add this to your package's `pubspec.yaml` file:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
ente_qr:
|
||||
path: path/to/ente_qr
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```dart
|
||||
import 'package:ente_qr/ente_qr.dart';
|
||||
|
||||
final qr = EnteQr();
|
||||
|
||||
// Scan QR code from an image file
|
||||
final result = await qr.scanQrFromImage('/path/to/image.jpg');
|
||||
|
||||
if (result.success) {
|
||||
print('QR Code content: ${result.content}');
|
||||
} else {
|
||||
print('Error: ${result.error}');
|
||||
}
|
||||
```
|
||||
|
||||
### Complete Example with File Picker
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ente_qr/ente_qr.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
|
||||
class QrScannerPage extends StatefulWidget {
|
||||
@override
|
||||
_QrScannerPageState createState() => _QrScannerPageState();
|
||||
}
|
||||
|
||||
class _QrScannerPageState extends State<QrScannerPage> {
|
||||
final _enteQr = EnteQr();
|
||||
String _result = 'No QR code scanned';
|
||||
|
||||
Future<void> _scanQrFromImage() async {
|
||||
// Pick an image file
|
||||
FilePickerResult? fileResult = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
allowMultiple: false,
|
||||
);
|
||||
|
||||
if (fileResult != null && fileResult.files.single.path != null) {
|
||||
final imagePath = fileResult.files.single.path!;
|
||||
|
||||
// Scan QR code from the selected image
|
||||
final qrResult = await _enteQr.scanQrFromImage(imagePath);
|
||||
|
||||
setState(() {
|
||||
if (qrResult.success) {
|
||||
_result = 'QR Code: ${qrResult.content}';
|
||||
} else {
|
||||
_result = 'Error: ${qrResult.error}';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('QR Scanner')),
|
||||
body: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: _scanQrFromImage,
|
||||
child: Text('Pick Image and Scan QR'),
|
||||
),
|
||||
Text(_result),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### EnteQr
|
||||
|
||||
The main class for QR code scanning operations.
|
||||
|
||||
#### Methods
|
||||
|
||||
##### `scanQrFromImage(String imagePath)`
|
||||
|
||||
Scans a QR code from an image file.
|
||||
|
||||
**Parameters:**
|
||||
- `imagePath` (String): The file path to the image containing the QR code
|
||||
|
||||
**Returns:**
|
||||
- `Future<QrScanResult>`: A result object containing either the QR code content or an error
|
||||
|
||||
### QrScanResult
|
||||
|
||||
The result object returned by QR scanning operations.
|
||||
|
||||
**Properties:**
|
||||
- `content` (String?): The QR code content if successful, null otherwise
|
||||
- `error` (String?): Error message if scanning failed, null otherwise
|
||||
- `success` (bool): Whether the scanning operation was successful
|
||||
|
||||
**Factory Constructors:**
|
||||
- `QrScanResult.success(String content)`: Creates a successful result
|
||||
- `QrScanResult.error(String error)`: Creates an error result
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Android
|
||||
|
||||
The Android implementation uses the ZXing library (com.google.zxing) for QR code detection:
|
||||
- `com.journeyapps:zxing-android-embedded:4.3.0`
|
||||
- `com.google.zxing:core:3.5.1`
|
||||
|
||||
### iOS
|
||||
|
||||
The iOS implementation uses Core Image framework's built-in QR code detection capabilities via `CIDetector`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
The plugin provides comprehensive error handling for common scenarios:
|
||||
- File not found
|
||||
- Invalid image format
|
||||
- No QR code found in image
|
||||
- Platform-specific errors
|
||||
- Unexpected errors
|
||||
|
||||
All errors are returned as part of the `QrScanResult` object with descriptive error messages.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the same license as the Ente project.
|
||||
1
mobile/apps/auth/plugins/qr/analysis_options.yaml
Normal file
1
mobile/apps/auth/plugins/qr/analysis_options.yaml
Normal file
@@ -0,0 +1 @@
|
||||
include: ../../analysis_options.yaml
|
||||
65
mobile/apps/auth/plugins/qr/android/build.gradle
Normal file
65
mobile/apps/auth/plugins/qr/android/build.gradle
Normal file
@@ -0,0 +1,65 @@
|
||||
group 'io.ente.auth.ente_qr'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
test.java.srcDirs += 'src/test/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
|
||||
implementation 'com.google.zxing:core:3.5.1'
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||
outputs.upToDateWhen {false}
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
mobile/apps/auth/plugins/qr/android/settings.gradle
Normal file
1
mobile/apps/auth/plugins/qr/android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'ente_qr'
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.ente.auth.ente_qr">
|
||||
</manifest>
|
||||
@@ -0,0 +1,138 @@
|
||||
package io.ente.auth.ente_qr
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.annotation.NonNull
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.BinaryBitmap
|
||||
import com.google.zxing.DecodeHintType
|
||||
import com.google.zxing.MultiFormatReader
|
||||
import com.google.zxing.NotFoundException
|
||||
import com.google.zxing.RGBLuminanceSource
|
||||
import com.google.zxing.Result as ZXingResult
|
||||
import com.google.zxing.common.HybridBinarizer
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/** EnteQrPlugin */
|
||||
class EnteQrPlugin: FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private lateinit var channel : MethodChannel
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "ente_qr")
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
when (call.method) {
|
||||
"getPlatformVersion" -> {
|
||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
||||
}
|
||||
"scanQrFromImage" -> {
|
||||
val imagePath = call.argument<String>("imagePath")
|
||||
if (imagePath == null) {
|
||||
result.success(mapOf(
|
||||
"success" to false,
|
||||
"error" to "Image path is required"
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val qrResult = scanQrCode(imagePath)
|
||||
result.success(qrResult)
|
||||
} catch (e: Exception) {
|
||||
result.success(mapOf(
|
||||
"success" to false,
|
||||
"error" to "Error scanning QR code: ${e.message}"
|
||||
))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanQrCode(imagePath: String): Map<String, Any> {
|
||||
try {
|
||||
val file = File(imagePath)
|
||||
if (!file.exists()) {
|
||||
return mapOf(
|
||||
"success" to false,
|
||||
"error" to "Image file not found: $imagePath"
|
||||
)
|
||||
}
|
||||
|
||||
var bitmap = BitmapFactory.decodeFile(imagePath)
|
||||
if (bitmap == null) {
|
||||
return mapOf(
|
||||
"success" to false,
|
||||
"error" to "Unable to decode image file"
|
||||
)
|
||||
}
|
||||
|
||||
// Try multiple times with different image sizes like Aegis does
|
||||
for (i in 0..2) {
|
||||
if (i != 0) {
|
||||
// Resize bitmap for subsequent attempts
|
||||
val newWidth = bitmap.width / (i * 2)
|
||||
val newHeight = bitmap.height / (i * 2)
|
||||
if (newWidth > 0 && newHeight > 0) {
|
||||
bitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val width = bitmap.width
|
||||
val height = bitmap.height
|
||||
val pixels = IntArray(width * height)
|
||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
||||
|
||||
val source = RGBLuminanceSource(width, height, pixels)
|
||||
val binaryBitmap = BinaryBitmap(HybridBinarizer(source))
|
||||
|
||||
val reader = MultiFormatReader()
|
||||
val hints = HashMap<DecodeHintType, Any>()
|
||||
hints[DecodeHintType.POSSIBLE_FORMATS] = listOf(BarcodeFormat.QR_CODE)
|
||||
hints[DecodeHintType.TRY_HARDER] = true
|
||||
hints[DecodeHintType.ALSO_INVERTED] = true
|
||||
reader.setHints(hints)
|
||||
|
||||
val qrResult: ZXingResult = reader.decode(binaryBitmap)
|
||||
|
||||
return mapOf(
|
||||
"success" to true,
|
||||
"content" to qrResult.text
|
||||
)
|
||||
} catch (e: NotFoundException) {
|
||||
// Continue to next iteration
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return mapOf(
|
||||
"success" to false,
|
||||
"error" to "No QR code found in image"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return mapOf(
|
||||
"success" to false,
|
||||
"error" to "Error scanning QR code: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
}
|
||||
77
mobile/apps/auth/plugins/qr/ios/Classes/EnteQrPlugin.swift
Normal file
77
mobile/apps/auth/plugins/qr/ios/Classes/EnteQrPlugin.swift
Normal file
@@ -0,0 +1,77 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
public class EnteQrPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "ente_qr", binaryMessenger: registrar.messenger())
|
||||
let instance = EnteQrPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "getPlatformVersion":
|
||||
result("iOS " + UIDevice.current.systemVersion)
|
||||
case "scanQrFromImage":
|
||||
guard let args = call.arguments as? [String: Any],
|
||||
let imagePath = args["imagePath"] as? String else {
|
||||
result([
|
||||
"success": false,
|
||||
"error": "Image path is required"
|
||||
])
|
||||
return
|
||||
}
|
||||
|
||||
scanQrCode(from: imagePath, result: result)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
private func scanQrCode(from imagePath: String, result: @escaping FlutterResult) {
|
||||
guard let image = UIImage(contentsOfFile: imagePath) else {
|
||||
result([
|
||||
"success": false,
|
||||
"error": "Unable to load image from path: \(imagePath)"
|
||||
])
|
||||
return
|
||||
}
|
||||
|
||||
guard let cgImage = image.cgImage else {
|
||||
result([
|
||||
"success": false,
|
||||
"error": "Unable to get CGImage from UIImage"
|
||||
])
|
||||
return
|
||||
}
|
||||
|
||||
let detector = CIDetector(ofType: CIDetectorTypeQRCode,
|
||||
context: nil,
|
||||
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
|
||||
|
||||
guard let qrDetector = detector else {
|
||||
result([
|
||||
"success": false,
|
||||
"error": "Unable to create QR code detector"
|
||||
])
|
||||
return
|
||||
}
|
||||
|
||||
let ciImage = CIImage(cgImage: cgImage)
|
||||
let features = qrDetector.features(in: ciImage)
|
||||
|
||||
if let qrFeature = features.first as? CIQRCodeFeature,
|
||||
let messageString = qrFeature.messageString {
|
||||
result([
|
||||
"success": true,
|
||||
"content": messageString
|
||||
])
|
||||
} else {
|
||||
result([
|
||||
"success": false,
|
||||
"error": "No QR code found in image"
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
23
mobile/apps/auth/plugins/qr/ios/ente_qr.podspec
Normal file
23
mobile/apps/auth/plugins/qr/ios/ente_qr.podspec
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint ente_qr.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ente_qr'
|
||||
s.version = '0.0.1'
|
||||
s.summary = 'A QR code reader plugin for Ente.'
|
||||
s.description = <<-DESC
|
||||
A QR code reader plugin for Ente.
|
||||
DESC
|
||||
s.homepage = 'https://ente.io'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Ente' => 'team@ente.io' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '9.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
||||
30
mobile/apps/auth/plugins/qr/lib/ente_qr.dart
Normal file
30
mobile/apps/auth/plugins/qr/lib/ente_qr.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:ente_qr/ente_qr_platform_interface.dart';
|
||||
|
||||
export 'ente_qr_platform_interface.dart' show QrScanResult;
|
||||
|
||||
class EnteQr {
|
||||
Future<String?> getPlatformVersion() {
|
||||
return EnteQrPlatform.instance.getPlatformVersion();
|
||||
}
|
||||
|
||||
/// Scans a QR code from an image file at the given path.
|
||||
///
|
||||
/// [imagePath] - The file path to the image containing the QR code
|
||||
///
|
||||
/// Returns a [QrScanResult] containing either the QR code content on success
|
||||
/// or an error message on failure.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final qr = EnteQr();
|
||||
/// final result = await qr.scanQrFromImage('/path/to/image.jpg');
|
||||
/// if (result.success) {
|
||||
/// print('QR Code content: ${result.content}');
|
||||
/// } else {
|
||||
/// print('Error: ${result.error}');
|
||||
/// }
|
||||
/// ```
|
||||
Future<QrScanResult> scanQrFromImage(String imagePath) {
|
||||
return EnteQrPlatform.instance.scanQrFromImage(imagePath);
|
||||
}
|
||||
}
|
||||
52
mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart
Normal file
52
mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:ente_qr/ente_qr_platform_interface.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// An implementation of [EnteQrPlatform] that uses method channels.
|
||||
class MethodChannelEnteQr extends EnteQrPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('ente_qr');
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() async {
|
||||
final version =
|
||||
await methodChannel.invokeMethod<String>('getPlatformVersion');
|
||||
return version;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<QrScanResult> scanQrFromImage(String imagePath) async {
|
||||
try {
|
||||
final dynamic result = await methodChannel.invokeMethod(
|
||||
'scanQrFromImage',
|
||||
{'imagePath': imagePath},
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return QrScanResult.error('Failed to scan QR code');
|
||||
}
|
||||
|
||||
// Convert to Map<String, dynamic> safely
|
||||
final Map<String, dynamic> resultMap =
|
||||
Map<String, dynamic>.from(result as Map);
|
||||
|
||||
final bool success = resultMap['success'] as bool? ?? false;
|
||||
if (success) {
|
||||
final String? content = resultMap['content'] as String?;
|
||||
if (content != null && content.isNotEmpty) {
|
||||
return QrScanResult.success(content);
|
||||
} else {
|
||||
return QrScanResult.error('No QR code found in image');
|
||||
}
|
||||
} else {
|
||||
final String? error = resultMap['error'] as String?;
|
||||
return QrScanResult.error(error ?? 'Unknown error occurred');
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
return QrScanResult.error('Platform error: ${e.message}');
|
||||
} catch (e) {
|
||||
return QrScanResult.error('Unexpected error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:ente_qr/ente_qr_method_channel.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
/// The result of QR code scanning
|
||||
class QrScanResult {
|
||||
final String? content;
|
||||
final String? error;
|
||||
final bool success;
|
||||
|
||||
const QrScanResult({
|
||||
this.content,
|
||||
this.error,
|
||||
required this.success,
|
||||
});
|
||||
|
||||
factory QrScanResult.success(String content) {
|
||||
return QrScanResult(
|
||||
content: content,
|
||||
success: true,
|
||||
);
|
||||
}
|
||||
|
||||
factory QrScanResult.error(String error) {
|
||||
return QrScanResult(
|
||||
error: error,
|
||||
success: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class EnteQrPlatform extends PlatformInterface {
|
||||
/// Constructs a EnteQrPlatform.
|
||||
EnteQrPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static EnteQrPlatform _instance = MethodChannelEnteQr();
|
||||
|
||||
/// The default instance of [EnteQrPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelEnteQr].
|
||||
static EnteQrPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [EnteQrPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(EnteQrPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<String?> getPlatformVersion() {
|
||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Scans a QR code from an image file at the given path.
|
||||
/// Returns the QR code content as a string if successful, null otherwise.
|
||||
Future<QrScanResult> scanQrFromImage(String imagePath) {
|
||||
throw UnimplementedError('scanQrFromImage() has not been implemented.');
|
||||
}
|
||||
}
|
||||
213
mobile/apps/auth/plugins/qr/pubspec.lock
Normal file
213
mobile/apps/auth/plugins/qr/pubspec.lock
Normal file
@@ -0,0 +1,213 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
plugin_platform_interface:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
29
mobile/apps/auth/plugins/qr/pubspec.yaml
Normal file
29
mobile/apps/auth/plugins/qr/pubspec.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: ente_qr
|
||||
description: "A QR code reader plugin for Ente."
|
||||
version: 0.0.1
|
||||
homepage: https://ente.io
|
||||
|
||||
environment:
|
||||
sdk: '>=3.4.3 <4.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
plugin_platform_interface: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: io.ente.auth.ente_qr
|
||||
pluginClass: EnteQrPlugin
|
||||
ios:
|
||||
pluginClass: EnteQrPlugin
|
||||
32
mobile/apps/auth/plugins/qr/test/ente_qr_test.dart
Normal file
32
mobile/apps/auth/plugins/qr/test/ente_qr_test.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:ente_qr/ente_qr.dart';
|
||||
import 'package:ente_qr/ente_qr_method_channel.dart';
|
||||
import 'package:ente_qr/ente_qr_platform_interface.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
class MockEnteQrPlatform
|
||||
with MockPlatformInterfaceMixin
|
||||
implements EnteQrPlatform {
|
||||
@override
|
||||
Future<String?> getPlatformVersion() => Future.value('42');
|
||||
|
||||
@override
|
||||
Future<QrScanResult> scanQrFromImage(String imagePath) =>
|
||||
Future.value(QrScanResult.error('Mock implementation'));
|
||||
}
|
||||
|
||||
void main() {
|
||||
final EnteQrPlatform initialPlatform = EnteQrPlatform.instance;
|
||||
|
||||
test('$MethodChannelEnteQr is the default instance', () {
|
||||
expect(initialPlatform, isInstanceOf<MethodChannelEnteQr>());
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
final EnteQr enteQrPlugin = EnteQr();
|
||||
final MockEnteQrPlatform fakePlatform = MockEnteQrPlatform();
|
||||
EnteQrPlatform.instance = fakePlatform;
|
||||
|
||||
expect(await enteQrPlugin.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
@@ -393,6 +393,27 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
ente_accounts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/accounts"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_base:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/base"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_configuration:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/configuration"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_crypto_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -402,6 +423,62 @@ packages:
|
||||
url: "https://github.com/ente-io/ente_crypto_dart.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
ente_events:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/events"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_lock_screen:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/lock_screen"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_logging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/logging"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_network:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/network"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_qr:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "plugins/qr"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
ente_strings:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/strings"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_ui:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../packages/ui"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
ente_utils:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
path: "../../packages/utils"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
event_bus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -462,18 +539,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: ef9908739bdd9c476353d6adff72e88fd00c625f5b959ae23f7567bd5137db0a
|
||||
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
version: "10.3.2"
|
||||
file_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_saver
|
||||
sha256: "448b1e30142cffe52f37ee085ea9ca50670d5425bb09b649d193549b2dcf6e26"
|
||||
sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.3.1"
|
||||
fixnum:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -485,10 +562,11 @@ packages:
|
||||
fk_user_agent:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fk_user_agent
|
||||
sha256: fd6c94e120786985a292d12f61422a581f4e851148d5940af38b819357b8ad0d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "."
|
||||
ref: "458046cd9a88924e5074d96ba45397219d53b230"
|
||||
resolved-ref: "458046cd9a88924e5074d96ba45397219d53b230"
|
||||
url: "https://github.com/flutter-fast-kit/fk_user_agent"
|
||||
source: git
|
||||
version: "2.1.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
@@ -538,21 +616,19 @@ packages:
|
||||
flutter_inappwebview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: flutter_inappwebview
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "6.2.0-beta.3"
|
||||
name: flutter_inappwebview
|
||||
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
flutter_inappwebview_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_android
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "1.2.0-beta.3"
|
||||
name: flutter_inappwebview_android
|
||||
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
flutter_inappwebview_internal_annotations:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -564,48 +640,43 @@ packages:
|
||||
flutter_inappwebview_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_ios
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "1.2.0-beta.3"
|
||||
name: flutter_inappwebview_ios
|
||||
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
flutter_inappwebview_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_macos
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "1.2.0-beta.3"
|
||||
name: flutter_inappwebview_macos
|
||||
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
flutter_inappwebview_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_platform_interface
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "1.4.0-beta.3"
|
||||
name: flutter_inappwebview_platform_interface
|
||||
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0+1"
|
||||
flutter_inappwebview_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_web
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "1.2.0-beta.3"
|
||||
name: flutter_inappwebview_web
|
||||
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
flutter_inappwebview_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: flutter_inappwebview_windows
|
||||
ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26"
|
||||
url: "https://github.com/pichillilorenzo/flutter_inappwebview.git"
|
||||
source: git
|
||||
version: "0.7.0-beta.3"
|
||||
name: flutter_inappwebview_windows
|
||||
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -887,7 +958,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
image:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: image
|
||||
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||
@@ -1081,10 +1152,11 @@ packages:
|
||||
move_to_background:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: move_to_background
|
||||
sha256: "00caad17a6ce149910777131503f43f8ed80025681f94684e3a6a87d979b914c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "."
|
||||
ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d"
|
||||
resolved-ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d"
|
||||
url: "https://github.com/Sayegh7/move_to_background"
|
||||
source: git
|
||||
version: "1.0.2"
|
||||
native_dio_adapter:
|
||||
dependency: "direct main"
|
||||
@@ -1329,11 +1401,12 @@ packages:
|
||||
qr_code_scanner:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: qr_code_scanner
|
||||
sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
path: "."
|
||||
ref: a2d31633d4744f72ada87cfa85d221358ab082af
|
||||
resolved-ref: a2d31633d4744f72ada87cfa85d221358ab082af
|
||||
url: "https://github.com/juliuscanute/qr_code_scanner"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
qr_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1731,6 +1804,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
ua_client_hints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ua_client_hints
|
||||
sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
|
||||
name: ente_auth
|
||||
description: ente two-factor authenticator
|
||||
version: 4.4.3+443
|
||||
version: 4.4.7+447
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
adaptive_theme: ^3.1.0 # done
|
||||
adaptive_theme: ^3.1.0
|
||||
app_links: ^6.3.3
|
||||
archive: ^4.0.7
|
||||
auto_size_text: ^3.0.0
|
||||
base32: ^2.1.3
|
||||
bip39: ^1.0.6 #done
|
||||
bip39: ^1.0.6
|
||||
bloc: ^9.0.0
|
||||
clipboard: ^0.1.3
|
||||
collection: ^1.18.0 # dart
|
||||
@@ -25,19 +24,41 @@ dependencies:
|
||||
dotted_border: ^3.1.0
|
||||
dropdown_button2: ^2.3.9
|
||||
email_validator: ^3.0.0
|
||||
ente_accounts:
|
||||
path: ../../packages/accounts
|
||||
ente_base:
|
||||
path: ../../packages/base
|
||||
ente_configuration:
|
||||
path: ../../packages/configuration
|
||||
ente_crypto_dart:
|
||||
git:
|
||||
url: https://github.com/ente-io/ente_crypto_dart.git
|
||||
ente_events:
|
||||
path: ../../packages/events
|
||||
ente_lock_screen:
|
||||
path: ../../packages/lock_screen
|
||||
ente_logging:
|
||||
path: ../../packages/logging
|
||||
ente_network:
|
||||
path: ../../packages/network
|
||||
ente_qr:
|
||||
path: plugins/qr
|
||||
ente_strings:
|
||||
path: ../../packages/strings
|
||||
ente_ui:
|
||||
path: ../../packages/ui
|
||||
event_bus: ^2.0.0
|
||||
expandable: ^5.0.1
|
||||
expansion_tile_card: ^3.0.0
|
||||
ffi: ^2.1.0
|
||||
figma_squircle: ^0.6.3
|
||||
file_picker: ^10.2.0
|
||||
# https://github.com/incrediblezayed/file_saver/issues/86
|
||||
file_saver: ^0.3.0
|
||||
file_picker: ^10.3.2
|
||||
file_saver: ^0.3.1
|
||||
fixnum: ^1.1.0
|
||||
fk_user_agent: ^2.1.0
|
||||
fk_user_agent: # no package updates on pub.dev
|
||||
git:
|
||||
url: https://github.com/flutter-fast-kit/fk_user_agent
|
||||
ref: 458046cd9a88924e5074d96ba45397219d53b230
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_animate: ^4.1.0
|
||||
@@ -47,13 +68,9 @@ dependencies:
|
||||
flutter_email_sender: ^7.0.0
|
||||
# revert to pub.dev when merged
|
||||
# https://github.com/pichillilorenzo/flutter_inappwebview/pull/2548
|
||||
flutter_inappwebview:
|
||||
git:
|
||||
url: https://github.com/pichillilorenzo/flutter_inappwebview.git
|
||||
path: flutter_inappwebview
|
||||
ref: 3e6c4c4a25340cd363af9d38891d88498b90be26
|
||||
flutter_inappwebview: ^6.1.4
|
||||
flutter_launcher_icons: ^0.14.1
|
||||
flutter_local_authentication:
|
||||
flutter_local_authentication: # linux fprintd fix is not published on pub.dev
|
||||
git:
|
||||
url: https://github.com/eaceto/flutter_local_authentication
|
||||
ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96
|
||||
@@ -69,6 +86,7 @@ dependencies:
|
||||
google_nav_bar: ^5.0.5 #supported
|
||||
gradient_borders: ^1.0.0
|
||||
http: ^1.1.0
|
||||
image: ^4.5.4
|
||||
intl: ^0.20.2
|
||||
io: ^1.0.4
|
||||
json_annotation: ^4.5.0
|
||||
@@ -77,7 +95,10 @@ dependencies:
|
||||
local_auth_darwin: ^1.2.2
|
||||
logging: ^1.0.1
|
||||
modal_bottom_sheet: ^3.0.0
|
||||
move_to_background: ^1.0.2
|
||||
move_to_background: # no package updates on pub.dev
|
||||
git:
|
||||
url: https://github.com/Sayegh7/move_to_background
|
||||
ref: 91e4d1a9c55b28bf93425d1f12faf410efc1e48d
|
||||
native_dio_adapter: ^1.4.0
|
||||
otp: ^3.1.1
|
||||
package_info_plus: ^8.0.2
|
||||
@@ -88,8 +109,11 @@ dependencies:
|
||||
pointycastle: ^3.7.3
|
||||
privacy_screen: ^0.0.6
|
||||
protobuf: ^4.1.0
|
||||
qr_code_scanner: ^1.0.1
|
||||
qr_flutter: ^4.1.0
|
||||
qr_code_scanner: # no package updates on pub.dev
|
||||
git:
|
||||
url: https://github.com/juliuscanute/qr_code_scanner
|
||||
ref: a2d31633d4744f72ada87cfa85d221358ab082af
|
||||
qr_flutter: ^4.1.0
|
||||
sentry: ^8.14.2
|
||||
sentry_flutter: ^8.14.2
|
||||
share_plus: ^11.0.0
|
||||
|
||||
22
mobile/apps/auth/pubspec_overrides.yaml
Normal file
22
mobile/apps/auth/pubspec_overrides.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
# melos_managed_dependency_overrides: ente_accounts,ente_base,ente_configuration,ente_events,ente_lock_screen,ente_logging,ente_network,ente_strings,ente_ui,ente_utils
|
||||
dependency_overrides:
|
||||
ente_accounts:
|
||||
path: ../../packages/accounts
|
||||
ente_base:
|
||||
path: ../../packages/base
|
||||
ente_configuration:
|
||||
path: ../../packages/configuration
|
||||
ente_events:
|
||||
path: ../../packages/events
|
||||
ente_lock_screen:
|
||||
path: ../../packages/lock_screen
|
||||
ente_logging:
|
||||
path: ../../packages/logging
|
||||
ente_network:
|
||||
path: ../../packages/network
|
||||
ente_strings:
|
||||
path: ../../packages/strings
|
||||
ente_ui:
|
||||
path: ../../packages/ui
|
||||
ente_utils:
|
||||
path: ../../packages/utils
|
||||
54
mobile/apps/locker/.gitignore
vendored
Normal file
54
mobile/apps/locker/.gitignore
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# Let folks use their custom editor settings
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# Editors
|
||||
.vscode/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Exceptions to above rules.
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
|
||||
android/key.properties
|
||||
android/app/.settings/*
|
||||
android/.settings/
|
||||
.env
|
||||
|
||||
fastlane/report.xml
|
||||
|
||||
# Android related
|
||||
android/app/build/
|
||||
|
||||
devtools_options.yaml
|
||||
7
mobile/apps/locker/README.md
Normal file
7
mobile/apps/locker/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ente Locker
|
||||
|
||||
## TODOs
|
||||
|
||||
Refactor and merge
|
||||
- [ ] Verify correctness for `PackageInfoUtil.getPackageName()` on Linux and Windows
|
||||
- [ ] Update `file_url.dart` to download only via CF worker when necessary
|
||||
1
mobile/apps/locker/analysis_options.yaml
Normal file
1
mobile/apps/locker/analysis_options.yaml
Normal file
@@ -0,0 +1 @@
|
||||
include: ../../analysis_options.yaml
|
||||
13
mobile/apps/locker/android/.gitignore
vendored
Normal file
13
mobile/apps/locker/android/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/to/reference-keystore
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
44
mobile/apps/locker/android/app/build.gradle
Normal file
44
mobile/apps/locker/android/app/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.ente.locker"
|
||||
compileSdk = 35
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "io.ente.locker"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = 26
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 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"/>
|
||||
</manifest>
|
||||
57
mobile/apps/locker/android/app/src/main/AndroidManifest.xml
Normal file
57
mobile/apps/locker/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Camera permissions for document scanner -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
|
||||
<application
|
||||
android:label="Ente Locker"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/icon_blue">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="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
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.ente.locker
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user