diff --git a/.github/workflows/auth-crowdin.yml b/.github/workflows/auth-crowdin.yml index d472cf594..f269a8c8d 100644 --- a/.github/workflows/auth-crowdin.yml +++ b/.github/workflows/auth-crowdin.yml @@ -9,8 +9,8 @@ on: - ".github/workflows/auth-crowdin.yml" branches: [main] schedule: - # See: [Note: Run every 24 hours] - - cron: "50 1 * * *" + # See: [Note: Run workflow on specific days of the week] + - cron: "50 1 * * 2,5" # Also allow manually running the workflow workflow_dispatch: diff --git a/.github/workflows/mobile-crowdin.yml b/.github/workflows/mobile-crowdin.yml index e12ab9aac..35b4c3876 100644 --- a/.github/workflows/mobile-crowdin.yml +++ b/.github/workflows/mobile-crowdin.yml @@ -9,8 +9,8 @@ on: - ".github/workflows/mobile-crowdin.yml" branches: [main] schedule: - # See: [Note: Run every 24 hours] - - cron: "40 1 * * *" + # See: [Note: Run workflow on specific days of the week] + - cron: "40 1 * * 2,5" # Also allow manually running the workflow workflow_dispatch: diff --git a/.github/workflows/mobile-lint.yml b/.github/workflows/mobile-lint.yml index 7b5e5fbed..cbbbbcfbb 100644 --- a/.github/workflows/mobile-lint.yml +++ b/.github/workflows/mobile-lint.yml @@ -3,7 +3,7 @@ name: "Lint (mobile)" on: # Run on every push to a branch other than main that changes mobile/ push: - branches-ignore: [main] + branches-ignore: [main, f-droid] paths: - "mobile/**" - ".github/workflows/mobile-lint.yml" diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml index fc910fbb8..3ce4db8d2 100644 --- a/.github/workflows/mobile-release.yml +++ b/.github/workflows/mobile-release.yml @@ -49,7 +49,7 @@ jobs: SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }} - name: Checksum - run: sha256sum build/app/outputs/flutter-apk/ente.apk > build/app/outputs/flutter-apk/sha256sum + run: sha256sum build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk > build/app/outputs/flutter-apk/sha256sum - name: Create a draft GitHub release uses: ncipollo/release-action@v1 diff --git a/.github/workflows/web-crowdin.yml b/.github/workflows/web-crowdin.yml index c8acb2322..8733167d6 100644 --- a/.github/workflows/web-crowdin.yml +++ b/.github/workflows/web-crowdin.yml @@ -9,8 +9,14 @@ on: - ".github/workflows/web-crowdin.yml" branches: [main] schedule: - # See: [Note: Run every 24 hours] - - cron: "20 1 * * *" + # [Note: Run workflow on specific days of the week] + # + # The last (5th) component of the cron syntax denotes the day of the + # week, with 0 == SUN and 6 == SAT. So, for example, to run on every TUE + # and FRI, this can be set to `2,5`. + # + # See also: [Note: Run workflow every 24 hours] + - cron: "20 1 * * 2,5" # Also allow manually running the workflow workflow_dispatch: diff --git a/.github/workflows/web-nightly.yml b/.github/workflows/web-nightly.yml index 3672b8b48..a800a4b73 100644 --- a/.github/workflows/web-nightly.yml +++ b/.github/workflows/web-nightly.yml @@ -2,7 +2,7 @@ name: "Nightly (web)" on: schedule: - # [Note: Run every 24 hours] + # [Note: Run workflow every 24 hours] # # Run every 24 hours - First field is minute, second is hour of the day # This runs 23:15 UTC everyday - 1 and 15 are just arbitrary offset to diff --git a/.gitmodules b/.gitmodules index 36834f2cd..cfea8359b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,16 +9,9 @@ [submodule "auth/assets/simple-icons"] path = auth/assets/simple-icons url = https://github.com/simple-icons/simple-icons.git -[submodule "mobile/thirdparty/flutter"] - path = mobile/thirdparty/flutter - url = https://github.com/flutter/flutter.git - branch = stable [submodule "mobile/plugins/clip_ggml"] path = mobile/plugins/clip_ggml url = https://github.com/ente-io/clip-ggml.git -[submodule "mobile/thirdparty/isar"] - path = mobile/thirdparty/isar - url = https://github.com/isar/isar [submodule "web/apps/photos/thirdparty/ffmpeg-wasm"] path = web/apps/photos/thirdparty/ffmpeg-wasm url = https://github.com/abhinavkgrd/ffmpeg.wasm.git diff --git a/auth/.gitignore b/auth/.gitignore index b909f8341..3d6f77b84 100644 --- a/auth/.gitignore +++ b/auth/.gitignore @@ -9,6 +9,9 @@ .history .svn/ +# Editors +.vscode/ + # IntelliJ related *.iml *.ipr diff --git a/auth/docs/release.md b/auth/docs/release.md index afc80076a..4b31c72f0 100644 --- a/auth/docs/release.md +++ b/auth/docs/release.md @@ -1,7 +1,14 @@ # Releases -Create a PR to bump up the version in `pubspec.yaml`. Once that is merged, tag -main, and push the tag. +Create a PR to bump up the version in `pubspec.yaml`. + +> [!NOTE] +> +> Use [semver](https://semver.org/) for the tags, with `auth-` as a prefix. +> Multiple beta releases for the same upcoming version can be done by adding +> build metadata at the end, e.g. `auth-v1.2.3-beta+3`. + +Once that is merged, tag main, and push the tag. ```sh git tag auth-v1.2.3 @@ -16,6 +23,11 @@ This'll trigger a GitHub workflow that: * Creates a new release in the internal track on Play Store. Once the workflow completes, go to the draft GitHub release that was created. + +> [!NOTE] +> +> Keep the title of the release same as the tag. + Set "Previous tag" to the last release of auth and press "Generate release notes". The generated release note will contain all PRs and new contributors from all the releases in the monorepo, so you'll need to filter them to keep diff --git a/auth/lib/l10n/arb/app_bg.arb b/auth/lib/l10n/arb/app_bg.arb new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/auth/lib/l10n/arb/app_bg.arb @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/auth/lib/l10n/arb/app_de.arb b/auth/lib/l10n/arb/app_de.arb index 6f9382540..32507899c 100644 --- a/auth/lib/l10n/arb/app_de.arb +++ b/auth/lib/l10n/arb/app_de.arb @@ -144,6 +144,7 @@ "enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.", "lostDeviceTitle": "Gerät verloren?", "twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung", + "passkeyAuthTitle": "Passkey Authentifizierung", "recoverAccount": "Konto wiederherstellen", "enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein", "recover": "Wiederherstellen", @@ -404,5 +405,14 @@ "signOutOtherDevices": "Andere Geräte abmelden", "doNotSignOut": "Nicht abmelden", "hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)", - "hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!" + "hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!", + "waitingForBrowserRequest": "Warten auf Browseranfrage...", + "passkey": "Passkey", + "developerSettingsWarning": "Sind Sie sicher, dass Sie die Entwicklereinstellungen ändern möchten?", + "developerSettings": "Entwicklereinstellungen", + "serverEndpoint": "Server Endpunkt", + "invalidEndpoint": "Ungültiger Endpunkt", + "invalidEndpointMessage": "Der eingegebene Endpunkt ist ungültig. Bitte geben Sie einen gültigen Endpunkt ein und versuchen Sie es erneut.", + "endpointUpdatedMessage": "Endpunkt erfolgreich aktualisiert", + "customEndpoint": "Mit {endpoint} verbunden" } \ No newline at end of file diff --git a/auth/lib/l10n/arb/app_ja.arb b/auth/lib/l10n/arb/app_ja.arb index 104da4a22..5b281747f 100644 --- a/auth/lib/l10n/arb/app_ja.arb +++ b/auth/lib/l10n/arb/app_ja.arb @@ -144,6 +144,8 @@ "enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください", "lostDeviceTitle": "デバイスを紛失しましたか?", "twoFactorAuthTitle": "2 要素認証", + "passkeyAuthTitle": "パスキー認証", + "verifyPasskey": "パスキーの認証", "recoverAccount": "アカウントを回復", "enterRecoveryKeyHint": "回復キーを入力", "recover": "回復", @@ -404,5 +406,15 @@ "signOutOtherDevices": "他のデバイスからサインアウトする", "doNotSignOut": "サインアウトしない", "hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)", - "hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!" + "hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!", + "waitingForBrowserRequest": "ブラウザのリクエストを待っています...", + "waitingForVerification": "認証を待っています...", + "passkey": "パスキー", + "developerSettingsWarning": "開発者向け設定を変更してもよろしいですか?", + "developerSettings": "開発者向け設定", + "serverEndpoint": "サーバーエンドポイント", + "invalidEndpoint": "無効なエンドポイントです", + "invalidEndpointMessage": "入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。", + "endpointUpdatedMessage": "エンドポイントの更新に成功しました", + "customEndpoint": "{endpoint} に接続しました" } \ No newline at end of file diff --git a/auth/lib/l10n/arb/app_pt.arb b/auth/lib/l10n/arb/app_pt.arb index a3f5262e7..aa6a2a837 100644 --- a/auth/lib/l10n/arb/app_pt.arb +++ b/auth/lib/l10n/arb/app_pt.arb @@ -144,6 +144,7 @@ "enterCodeHint": "Digite o código de 6 dígitos de\nseu aplicativo autenticador", "lostDeviceTitle": "Perdeu seu dispositivo?", "twoFactorAuthTitle": "Autenticação de dois fatores", + "passkeyAuthTitle": "Autenticação via Chave de acesso", "recoverAccount": "Recuperar conta", "enterRecoveryKeyHint": "Digite sua chave de recuperação", "recover": "Recuperar", @@ -404,5 +405,14 @@ "signOutOtherDevices": "Terminar sessão em outros dispositivos", "doNotSignOut": "Não encerrar sessão", "hearUsWhereTitle": "Como você ouviu sobre o Ente? (opcional)", - "hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!" + "hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!", + "waitingForBrowserRequest": "Aguardando solicitação do navegador...", + "passkey": "Chave de acesso", + "developerSettingsWarning": "Tem certeza de que deseja modificar as configurações de Desenvolvedor?", + "developerSettings": "Configurações de desenvolvedor", + "serverEndpoint": "Endpoint do servidor", + "invalidEndpoint": "Endpoint inválido", + "invalidEndpointMessage": "Desculpe, o endpoint que você inseriu é inválido. Por favor, insira um endpoint válido e tente novamente.", + "endpointUpdatedMessage": "Endpoint atualizado com sucesso", + "customEndpoint": "Conectado a {endpoint}" } \ No newline at end of file diff --git a/auth/lib/l10n/arb/app_sv.arb b/auth/lib/l10n/arb/app_sv.arb index faaff39cb..d99ed5f5f 100644 --- a/auth/lib/l10n/arb/app_sv.arb +++ b/auth/lib/l10n/arb/app_sv.arb @@ -131,6 +131,16 @@ "about": "Om", "terms": "Villkor", "warning": "Varning", + "importSuccessDesc": "Du har importerat {count} koder!", + "@importSuccessDesc": { + "placeholders": { + "count": { + "description": "The number of codes imported", + "type": "int", + "example": "1" + } + } + }, "pendingSyncs": "Varning", "activeSessions": "Aktiva sessioner", "enterPassword": "Ange lösenord", @@ -143,5 +153,7 @@ "iOSOkButton": "OK", "@iOSOkButton": { "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." - } + }, + "noInternetConnection": "Ingen internetanslutning", + "pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen." } \ No newline at end of file diff --git a/auth/lib/l10n/arb/app_zh.arb b/auth/lib/l10n/arb/app_zh.arb index 6d30306b0..e07e7dcfa 100644 --- a/auth/lib/l10n/arb/app_zh.arb +++ b/auth/lib/l10n/arb/app_zh.arb @@ -407,6 +407,12 @@ "hearUsWhereTitle": "您是如何知道Ente的? (可选的)", "hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!", "waitingForBrowserRequest": "正在等待浏览器请求...", - "launchPasskeyUrlAgain": "再次启动 通行密钥 URL", - "passkey": "通行密钥" + "passkey": "通行密钥", + "developerSettingsWarning": "您确定要修改开发者设置吗?", + "developerSettings": "开发者设置", + "serverEndpoint": "服务器端点", + "invalidEndpoint": "端点无效", + "invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。", + "endpointUpdatedMessage": "端点更新成功", + "customEndpoint": "已连接至 {endpoint}" } \ No newline at end of file diff --git a/auth/migration-guides/README.md b/auth/migration-guides/README.md new file mode 100644 index 000000000..56d8983d0 --- /dev/null +++ b/auth/migration-guides/README.md @@ -0,0 +1,4 @@ +Migration guides have moved to the [help +docs](https://help.ente.io/auth/migration-guides/). This folder just contains +redirects for old links. + diff --git a/auth/migration-guides/authy.md b/auth/migration-guides/authy.md index 4fbc7377f..630bc83c7 100644 --- a/auth/migration-guides/authy.md +++ b/auth/migration-guides/authy.md @@ -1,62 +1,2 @@ -# Migrating from Authy -A guide written by Green, an ente.io lover - ---- - -Migrating from Authy can be tiring, as you cannot export your 2FA codes through the app, meaning that you would have to reconfigure 2FA for all of your accounts for your new 2FA authenticator. But do not fear, as there is a much simpler way to migrate from Authy to ente! - -A user on GitHub has written a guide to export our data from Authy (morpheus on Discord found this and showed it to us), so we are going to be using that for the migration. - -## Exporting from Authy -To export your data, please follow [this guide](https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93). This will create a new JSON file with all your Authy TOTP data in it. **Do not share this file with anyone!** - -Or, you can [use this tool by Neeraj](https://github.com/ua741/authy-export/releases/tag/v0.0.4) to simplify things and skip directly to importing to ente Authenticator. -### *Do note that these tools may not export ALL of your codes. Make sure that all your accounts have been imported successfully before deleting any codes from your Authy account!* - -## Converting the export for ente Authenticator -### Update: You can now directly import from Bitwarden JSON export, meaning you can skip this step! If it doesn't work for some reason, though, then continue with this step. -So now that you have the JSON file, does that mean it can be imported into ente Authenticator? Yes, but if it doesn't work for some reason, then nope. (If you have a TXT file in the format ente Authenticator asked you for instead, then you probably used Neeraj's tool, so you can skip this step.) - -This is because the code in the guide exports your Authy data for Bitwarden, not ente Authenticator. If you have opened the JSON file, you might have noticed that the file created is not in a format that ente Authenticator asks for: - -ente Authenticator Screenshot - -So, this means that even if you try to import this file, nothing will happen. But don't worry, I've written a program in Python that converts the JSON file into a TXT file that ente Authenticator can use! (It's definitely not written **professionaly**, but hey it gets the job done so I'm happy with that.) - -You can download my program [here](https://github.com/gweeeen/ducky/blob/main/duckys_other_stuff/authy_to_ente.py). Or if you **really like making life hard**, then you can make a new Python file and copy this code to it: - -```py -import json -import os - -totp = [] - -accounts = json.load(open('authy-to-bitwarden-export.json','r',encoding='utf-8')) - -for account in accounts['items']: - totp.append(account['login']['totp']+'\n') - -writer = open('auth_codes.txt','w+',encoding='utf-8') -writer.writelines(totp) -writer.close() - -print('Saved to ' + os.getcwd() + '/auth_codes.txt') -``` - -To convert the file with this program, you will need to install [Python](https://www.python.org/downloads/) on your computer. - -Before you run the program, make sure that both the Python program and the JSON file are in the same directory, otherwise this will not work! - -To run the Python program, open it in IDLE and press F5, or open your terminal and type `python3 authy_to_ente.py` or `py -3 authy_to_ente.py`, depending on which OS you have. Once you run it, a new TXT file called `auth_codes.txt` will be generated. You can now import your data to ente Authenticator! - -## Importing to ente Authenticator -Now that we have the TXT file, let's import it. This should be the easiest part of the entire migration process. - -1. Copy the TXT file to one of your devices with ente Authenticator. -2. Log in to your account (if you haven't already). -3. Open the navigation menu (hamburger button on the top left), then press "Data", then press "Import codes". -4. Select the TXT file that was made earlier. - -And that's it! You have now successfully migrated from Authy to ente Authenticator. - -Just one more thing: Now that your secrets are safely stored, I recommend you delete the unencrypted JSON and TXT files that were made during the migration process for security. +Moved to +[help.ente.io/auth/migration-guides/authy](https://help.ente.io/auth/migration-guides/authy/) diff --git a/auth/migration-guides/encrypted_export.md b/auth/migration-guides/encrypted_export.md index b4e64134e..80a844c85 100644 --- a/auth/migration-guides/encrypted_export.md +++ b/auth/migration-guides/encrypted_export.md @@ -1,63 +1,2 @@ -# Auth Encrypted Export format - -## Overview - -When we export the auth codes, the data is encrypted using a key derived from the user's password. -This document describes the JSON structure used to organize exported data, including versioning and key derivation -parameters. - -## Export JSON Sample - -```json -{ - "version": 1, - "kdfParams": { - "memLimit": 4096, - "opsLimit": 3, - "salt": "example_salt" - }, - "encryptedData": "encrypted_data_here", - "encryptionNonce": "nonce_here" -} -``` - -The main object used to represent the export data. It contains the following key-value pairs: - -- `version`: The version of the export format. -- `kdfParams`: Key derivation function parameters. -- `encryptedData"`: The encrypted authentication data. -- `encryptionNonce`: The nonce used for encryption. - -### Version - -Export version is used to identify the format of the export data. - -#### Ver: 1 - -* KDF Algorithm: `ARGON2ID` -* Decrypted data format: `otpauth://totp/...`, separated by a new line. -* Encryption Algo: `XChaCha20-Poly1305` - -#### Key Derivation Function Params (KDF) - -This section contains the parameters that were using during KDF operation: - -- `memLimit`: Memory limit for the algorithm. -- `opsLimit`: Operations limit for the algorithm. -- `salt`: The salt used in the derivation process. - -#### Encrypted Data - -As mentioned above, the auth data is encrypted using a key that's derived by using user provided password & kdf params. -For encryption, we are using `XChaCha20-Poly1305` algorithm. - -## How to use the exported data - -* **Ente Authenticator app**: You can directly import the codes in the Ente Authenticator app. - > Settings -> Data -> Import Codes -> ente Encrypted export. - -* **Decrypt using Ente CLI** : Download the latest version of [Ente CLI](https://github.com/ente-io/ente/releases?q=CLI&expanded=false), and run the following command - -``` - ./ente auth decrypt -``` +Moved to +[help.ente.io/auth/migration-guides/export](https://help.ente.io/auth/migration-guides/export/) diff --git a/cli/.gitattributes b/cli/.gitattributes new file mode 100644 index 000000000..71965b858 --- /dev/null +++ b/cli/.gitattributes @@ -0,0 +1 @@ +docs/generated/*.md linguist-generated=true diff --git a/cli/README.md b/cli/README.md index 5a4097a46..8fc9aa694 100644 --- a/cli/README.md +++ b/cli/README.md @@ -7,7 +7,7 @@ use it to decrypting the export from Ente Auth. ## Install The easiest way is to download a pre-built binary from the [GitHub -releases](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0&expanded=true). +releases](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0). You can also build these binaries yourself diff --git a/cli/cmd/admin.go b/cli/cmd/admin.go index 0e41bbfe2..8a2d7f006 100644 --- a/cli/cmd/admin.go +++ b/cli/cmd/admin.go @@ -29,6 +29,9 @@ var _userDetailsCmd = &cobra.Command{ flags.UserEmail = f.Value.String() } }) + if flags.UserEmail == "" { + return fmt.Errorf("user email is required") + } return ctrl.GetUserId(context.Background(), *flags) }, } @@ -47,14 +50,55 @@ var _disable2faCmd = &cobra.Command{ flags.UserEmail = f.Value.String() } }) - fmt.Println("Not supported yet") - return nil + if flags.UserEmail == "" { + return fmt.Errorf("user email is required") + } + return ctrl.Disable2FA(context.Background(), *flags) + + }, +} + +var _deleteUser = &cobra.Command{ + Use: "delete-user", + Short: "Delete a user", + 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() + } + if f.Name == "user" { + flags.UserEmail = f.Value.String() + } + }) + if flags.UserEmail == "" { + return fmt.Errorf("user email is required") + } + return ctrl.DeleteUser(context.Background(), *flags) + + }, +} + +var _listUsers = &cobra.Command{ + Use: "list-users", + Short: "List all users", + 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.ListUsers(context.Background(), *flags) }, } var _updateFreeUserStorage = &cobra.Command{ Use: "update-subscription", - Short: "Update subscription for the free user", + Short: "Update subscription for user", + Long: "Update subscription for the free user. If you want to apply specific limits, use the `--no-limit False` flag", RunE: func(cmd *cobra.Command, args []string) error { recoverWithLog() var flags = &model.AdminActionForUser{} @@ -70,6 +114,9 @@ var _updateFreeUserStorage = &cobra.Command{ noLimit = strings.ToLower(f.Value.String()) == "true" } }) + if flags.UserEmail == "" { + return fmt.Errorf("user email is required") + } return ctrl.UpdateFreeStorage(context.Background(), *flags, noLimit) }, } @@ -78,13 +125,16 @@ func init() { rootCmd.AddCommand(_adminCmd) _ = _userDetailsCmd.MarkFlagRequired("admin-user") _ = _userDetailsCmd.MarkFlagRequired("user") - _userDetailsCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)") + _userDetailsCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. ") _userDetailsCmd.Flags().StringP("user", "u", "", "The email of the user to fetch details for. (required)") - _disable2faCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)") + _listUsers.Flags().StringP("admin-user", "a", "", "The email of the admin user. ") + _disable2faCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. ") _disable2faCmd.Flags().StringP("user", "u", "", "The email of the user to disable 2FA for. (required)") - _updateFreeUserStorage.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)") + _deleteUser.Flags().StringP("admin-user", "a", "", "The email of the admin user. ") + _deleteUser.Flags().StringP("user", "u", "", "The email of the user to delete. (required)") + _updateFreeUserStorage.Flags().StringP("admin-user", "a", "", "The email of the admin user.") _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, _updateFreeUserStorage) + _adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _updateFreeUserStorage, _listUsers, _deleteUser) } diff --git a/cli/docs/generated/ente.md b/cli/docs/generated/ente.md index 6d0263ce4..b9d3cde17 100644 --- a/cli/docs/generated/ente.md +++ b/cli/docs/generated/ente.md @@ -25,4 +25,4 @@ ente [flags] * [ente export](ente_export.md) - Starts the export process * [ente version](ente_version.md) - Prints the current version -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_account.md b/cli/docs/generated/ente_account.md index ec26f9557..c48a65336 100644 --- a/cli/docs/generated/ente_account.md +++ b/cli/docs/generated/ente_account.md @@ -16,4 +16,4 @@ Manage account settings * [ente account list](ente_account_list.md) - list configured accounts * [ente account update](ente_account_update.md) - Update an existing account's export directory -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_account_add.md b/cli/docs/generated/ente_account_add.md index 74b2c23f9..1904ca370 100644 --- a/cli/docs/generated/ente_account_add.md +++ b/cli/docs/generated/ente_account_add.md @@ -16,4 +16,4 @@ ente account add [flags] * [ente account](ente_account.md) - Manage account settings -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_account_get-token.md b/cli/docs/generated/ente_account_get-token.md index 58ef3e7cd..d7ee77255 100644 --- a/cli/docs/generated/ente_account_get-token.md +++ b/cli/docs/generated/ente_account_get-token.md @@ -18,4 +18,4 @@ ente account get-token [flags] * [ente account](ente_account.md) - Manage account settings -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_account_list.md b/cli/docs/generated/ente_account_list.md index 3fc6fbc2e..cfc59bb8d 100644 --- a/cli/docs/generated/ente_account_list.md +++ b/cli/docs/generated/ente_account_list.md @@ -16,4 +16,4 @@ ente account list [flags] * [ente account](ente_account.md) - Manage account settings -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_account_update.md b/cli/docs/generated/ente_account_update.md index 04c4418e7..acb65412a 100644 --- a/cli/docs/generated/ente_account_update.md +++ b/cli/docs/generated/ente_account_update.md @@ -19,4 +19,4 @@ ente account update [flags] * [ente account](ente_account.md) - Manage account settings -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin.md b/cli/docs/generated/ente_admin.md index 91e70324d..aafe51b39 100644 --- a/cli/docs/generated/ente_admin.md +++ b/cli/docs/generated/ente_admin.md @@ -15,8 +15,10 @@ Commands for admin actions like disable or enabling 2fa, bumping up the storage ### SEE ALSO * [ente](ente.md) - CLI tool for exporting your photos from ente.io +* [ente admin delete-user](ente_admin_delete-user.md) - Delete a user * [ente admin disable-2fa](ente_admin_disable-2fa.md) - Disable 2fa for a user * [ente admin get-user-id](ente_admin_get-user-id.md) - Get user id -* [ente admin update-subscription](ente_admin_update-subscription.md) - Update subscription for the free user +* [ente admin list-users](ente_admin_list-users.md) - List all users +* [ente admin update-subscription](ente_admin_update-subscription.md) - Update subscription for user -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin_delete-user.md b/cli/docs/generated/ente_admin_delete-user.md new file mode 100644 index 000000000..56c96841e --- /dev/null +++ b/cli/docs/generated/ente_admin_delete-user.md @@ -0,0 +1,21 @@ +## ente admin delete-user + +Delete a user + +``` +ente admin delete-user [flags] +``` + +### Options + +``` + -a, --admin-user string The email of the admin user. + -h, --help help for delete-user + -u, --user string The email of the user to delete. (required) +``` + +### SEE ALSO + +* [ente admin](ente_admin.md) - Commands for admin actions + +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin_disable-2fa.md b/cli/docs/generated/ente_admin_disable-2fa.md index 19183fdfe..333f0912e 100644 --- a/cli/docs/generated/ente_admin_disable-2fa.md +++ b/cli/docs/generated/ente_admin_disable-2fa.md @@ -9,7 +9,7 @@ ente admin disable-2fa [flags] ### Options ``` - -a, --admin-user string The email of the admin user. (required) + -a, --admin-user string The email of the admin user. -h, --help help for disable-2fa -u, --user string The email of the user to disable 2FA for. (required) ``` @@ -18,4 +18,4 @@ ente admin disable-2fa [flags] * [ente admin](ente_admin.md) - Commands for admin actions -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin_get-user-id.md b/cli/docs/generated/ente_admin_get-user-id.md index 0151a3ec8..3d26f624a 100644 --- a/cli/docs/generated/ente_admin_get-user-id.md +++ b/cli/docs/generated/ente_admin_get-user-id.md @@ -9,7 +9,7 @@ ente admin get-user-id [flags] ### Options ``` - -a, --admin-user string The email of the admin user. (required) + -a, --admin-user string The email of the admin user. -h, --help help for get-user-id -u, --user string The email of the user to fetch details for. (required) ``` @@ -18,4 +18,4 @@ ente admin get-user-id [flags] * [ente admin](ente_admin.md) - Commands for admin actions -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin_list-users.md b/cli/docs/generated/ente_admin_list-users.md new file mode 100644 index 000000000..8841df57b --- /dev/null +++ b/cli/docs/generated/ente_admin_list-users.md @@ -0,0 +1,20 @@ +## ente admin list-users + +List all users + +``` +ente admin list-users [flags] +``` + +### Options + +``` + -a, --admin-user string The email of the admin user. + -h, --help help for list-users +``` + +### SEE ALSO + +* [ente admin](ente_admin.md) - Commands for admin actions + +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_admin_update-subscription.md b/cli/docs/generated/ente_admin_update-subscription.md index 30339acf2..cc1fa9623 100644 --- a/cli/docs/generated/ente_admin_update-subscription.md +++ b/cli/docs/generated/ente_admin_update-subscription.md @@ -1,6 +1,10 @@ ## ente admin update-subscription -Update subscription for the free user +Update subscription for user + +### Synopsis + +Update subscription for the free user. If you want to apply specific limits, use the `--no-limit False` flag ``` ente admin update-subscription [flags] @@ -9,7 +13,7 @@ ente admin update-subscription [flags] ### Options ``` - -a, --admin-user string The email of the admin user. (required) + -a, --admin-user string The email of the admin user. -h, --help help for update-subscription --no-limit string When true, sets 100TB as storage limit, and expiry to current date + 100 years (default "True") -u, --user string The email of the user to update subscription for. (required) @@ -19,4 +23,4 @@ ente admin update-subscription [flags] * [ente admin](ente_admin.md) - Commands for admin actions -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_auth.md b/cli/docs/generated/ente_auth.md index 4a64a944d..5770f36f3 100644 --- a/cli/docs/generated/ente_auth.md +++ b/cli/docs/generated/ente_auth.md @@ -13,4 +13,4 @@ Authenticator commands * [ente](ente.md) - CLI tool for exporting your photos from ente.io * [ente auth decrypt](ente_auth_decrypt.md) - Decrypt authenticator export -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_auth_decrypt.md b/cli/docs/generated/ente_auth_decrypt.md index 1203319e9..e573db2a3 100644 --- a/cli/docs/generated/ente_auth_decrypt.md +++ b/cli/docs/generated/ente_auth_decrypt.md @@ -16,4 +16,4 @@ ente auth decrypt [input] [output] [flags] * [ente auth](ente_auth.md) - Authenticator commands -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_export.md b/cli/docs/generated/ente_export.md index fb4cc6541..c5783236c 100644 --- a/cli/docs/generated/ente_export.md +++ b/cli/docs/generated/ente_export.md @@ -16,4 +16,4 @@ ente export [flags] * [ente](ente.md) - CLI tool for exporting your photos from ente.io -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/generated/ente_version.md b/cli/docs/generated/ente_version.md index 0254e2ebd..b51055697 100644 --- a/cli/docs/generated/ente_version.md +++ b/cli/docs/generated/ente_version.md @@ -16,4 +16,4 @@ ente version [flags] * [ente](ente.md) - CLI tool for exporting your photos from ente.io -###### Auto generated by spf13/cobra on 13-Mar-2024 +###### Auto generated by spf13/cobra on 14-Mar-2024 diff --git a/cli/docs/release.md b/cli/docs/release.md index a539e7e5d..dce097eaf 100644 --- a/cli/docs/release.md +++ b/cli/docs/release.md @@ -2,6 +2,11 @@ Tag main, and push the tag. +> [!NOTE] +> +> See [auth/docs/release](../../auth/docs/release.md) for more details about the +> tag format. The prefix for cli releases should be `cli-`. + ```sh git tag cli-v1.2.3 git push origin cli-v1.2.3 diff --git a/cli/internal/api/admin.go b/cli/internal/api/admin.go index 1c0b2c50a..9e0bcb90a 100644 --- a/cli/internal/api/admin.go +++ b/cli/internal/api/admin.go @@ -25,6 +25,69 @@ func (c *Client) GetUserIdFromEmail(ctx context.Context, email string) (*models. } return &res, nil } + +func (c *Client) ListUsers(ctx context.Context) ([]models.User, error) { + var res struct { + Users []models.User `json:"users"` + } + r, err := c.restClient.R(). + SetContext(ctx). + SetQueryParam("sinceTime", "0"). + SetResult(&res). + Get("/admin/users/") + if err != nil { + return nil, err + } + if r.IsError() { + return nil, &ApiError{ + StatusCode: r.StatusCode(), + Message: r.String(), + } + } + return res.Users, nil +} + +func (c *Client) DeleteUser(ctx context.Context, email string) error { + + r, err := c.restClient.R(). + SetContext(ctx). + SetQueryParam("email", email). + Delete("/admin/user/delete") + if err != nil { + return err + } + if r.IsError() { + return &ApiError{ + StatusCode: r.StatusCode(), + Message: r.String(), + } + } + return nil +} + +func (c *Client) Disable2Fa(ctx context.Context, userID int64) error { + var res interface{} + + payload := map[string]interface{}{ + "userID": userID, + } + r, err := c.restClient.R(). + SetContext(ctx). + SetResult(&res). + SetBody(payload). + Post("/admin/user/disable-2fa") + if err != nil { + return err + } + if r.IsError() { + return &ApiError{ + StatusCode: r.StatusCode(), + Message: r.String(), + } + } + return nil +} + func (c *Client) UpdateFreePlanSub(ctx context.Context, userDetails *models.UserDetails, storageInBytes int64, expiryTimeInMicro int64) error { var res interface{} if userDetails.Subscription.ProductID != "free" { diff --git a/cli/internal/api/models/user_details.go b/cli/internal/api/models/user_details.go index 259ff972b..6a5310d7d 100644 --- a/cli/internal/api/models/user_details.go +++ b/cli/internal/api/models/user_details.go @@ -1,9 +1,7 @@ package models type UserDetails struct { - User struct { - ID int64 `json:"id"` - } `json:"user"` + User User `json:"user"` Usage int64 `json:"usage"` Email string `json:"email"` @@ -14,3 +12,10 @@ type UserDetails struct { PaymentProvider string `json:"paymentProvider"` } `json:"subscription"` } + +type User struct { + ID int64 + Email string `json:"email"` + Hash string `json:"hash"` + CreationTime int64 `json:"creationTime"` +} diff --git a/cli/pkg/admin_actions.go b/cli/pkg/admin_actions.go index c9ec00667..0105cdc19 100644 --- a/cli/pkg/admin_actions.go +++ b/cli/pkg/admin_actions.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/ente-io/cli/internal" + "github.com/ente-io/cli/internal/api" "github.com/ente-io/cli/pkg/model" "github.com/ente-io/cli/utils" "log" @@ -24,6 +25,63 @@ func (c *ClICtrl) GetUserId(ctx context.Context, params model.AdminActionForUser return nil } +func (c *ClICtrl) ListUsers(ctx context.Context, params model.AdminActionForUser) error { + accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail) + if err != nil { + return err + } + users, err := c.Client.ListUsers(accountCtx) + 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 + } + for _, user := range users { + fmt.Printf("Email: %s, ID: %d, Created: %s\n", user.Email, user.ID, time.UnixMicro(user.CreationTime).Format("2006-01-02")) + } + return nil +} + +func (c *ClICtrl) DeleteUser(ctx context.Context, params model.AdminActionForUser) error { + accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail) + if err != nil { + return err + } + err = c.Client.DeleteUser(accountCtx, params.UserEmail) + 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.Println("Successfully deleted user") + return nil +} + +func (c *ClICtrl) Disable2FA(ctx context.Context, params model.AdminActionForUser) error { + accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail) + if err != nil { + return err + } + userDetails, err := c.Client.GetUserIdFromEmail(accountCtx, params.UserEmail) + if err != nil { + return err + } + err = c.Client.Disable2Fa(accountCtx, userDetails.User.ID) + 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.Println("Successfully disabled 2FA for user") + return nil +} + func (c *ClICtrl) UpdateFreeStorage(ctx context.Context, params model.AdminActionForUser, noLimit bool) error { accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail) if err != nil { @@ -82,6 +140,9 @@ func (c *ClICtrl) buildAdminContext(ctx context.Context, adminEmail string) (con if err != nil { return nil, err } + if len(accounts) == 0 { + return nil, fmt.Errorf("no accounts found, use `account add` to add an account") + } var acc *model.Account for _, a := range accounts { if a.Email == adminEmail { @@ -89,6 +150,14 @@ func (c *ClICtrl) buildAdminContext(ctx context.Context, adminEmail string) (con break } } + if (len(accounts) > 1) && (acc == nil) { + return nil, fmt.Errorf("multiple accounts found, specify the admin email using --admin-user") + } + if acc == nil && len(accounts) == 1 { + acc = &accounts[0] + fmt.Printf("Assuming %s as the Admin \n------------\n", acc.Email) + } + if acc == nil { return nil, fmt.Errorf("account not found for %s, use `account list` to list accounts", adminEmail) } diff --git a/docs/docs/.vitepress/sidebar.ts b/docs/docs/.vitepress/sidebar.ts index 78a6733f4..ca531716b 100644 --- a/docs/docs/.vitepress/sidebar.ts +++ b/docs/docs/.vitepress/sidebar.ts @@ -69,9 +69,17 @@ export const sidebar = [ { text: "FAQ", link: "/auth/faq/" }, { text: "Migration guides", - collapsed: true, + collapsed: false, items: [ { text: "Introduction", link: "/auth/migration-guides/" }, + { + text: "From Authy", + link: "/auth/migration-guides/authy/", + }, + { + text: "Exporting your data", + link: "/auth/migration-guides/export", + }, ], }, ], @@ -86,9 +94,14 @@ export const sidebar = [ items: [ { text: "Introduction", link: "/self-hosting/guides/" }, { - text: "Configure custom server", + text: "Connect to custom server", link: "/self-hosting/guides/custom-server/", }, + { + text: "Administering your server", + link: "/self-hosting/guides/admin", + }, + { text: "Mobile build", link: "/self-hosting/guides/mobile-build", @@ -110,6 +123,10 @@ export const sidebar = [ text: "Verification code", link: "/self-hosting/faq/otp", }, + { + text: "Increase storage space", + link: "/self-hosting/faq/storage-space", + }, ], }, { diff --git a/docs/docs/auth/faq/index.md b/docs/docs/auth/faq/index.md index e40d18431..23564e2e3 100644 --- a/docs/docs/auth/faq/index.md +++ b/docs/docs/auth/faq/index.md @@ -1,21 +1,33 @@ - +--- +title: FAQ - Auth +description: Frequently asked questions about Ente Auth +--- # Frequently Asked Questions -### How secure is ente Auth? -All codes you backup via Ente is stored end-to-end encrypted. This means only you can access your codes. Our apps are open source and our cryptography has been externally audited. +### How secure is Ente Auth? + +All codes you backup via Ente is stored end-to-end encrypted. This means only +you can access your codes. Our apps are open source and our cryptography has +been externally audited. ### Can I access my codes on desktop? -You can access your codes on the web @ [auth.ente.io](https://auth.ente.io). + +You can access your codes on the web at [auth.ente.io](https://auth.ente.io). ### How can I delete or edit codes? + You can delete or edit a code by swiping left on that item. ### How can I support this project? -You can support the development of this project by subscribing to our Photos app @ [ente.io](https://ente.io). + +You can support the development of this project by subscribing to our Photos app +at [ente.io](https://ente.io). ### How can I enable FaceID lock in Ente Auth? + You can enable FaceID lock under Settings → Security → Lockscreen. ### Why does the desktop and mobile app displays different code? + Please verify that the time on both your mobile and desktop is same. diff --git a/docs/docs/auth/index.md b/docs/docs/auth/index.md index 11fb333af..8800c5422 100644 --- a/docs/docs/auth/index.md +++ b/docs/docs/auth/index.md @@ -7,8 +7,3 @@ description: User guide for Ente Auth Ente Auth is a free, cross-platform, end-to-end encrypted authenticator app. You can use it to safely store your 2FA codes (second-factor authentication codes). - -> [!CAUTION] -> -> These docs are still incomplete. If you feel like documenting an issue you ran -> into and then found a solution to, help us [fill them in](/about/contribute). diff --git a/docs/docs/auth/migration-guides/authy/index.md b/docs/docs/auth/migration-guides/authy/index.md new file mode 100644 index 000000000..d215a610b --- /dev/null +++ b/docs/docs/auth/migration-guides/authy/index.md @@ -0,0 +1,179 @@ +--- +title: Migrating from Authy +description: Guide for importing your existing Authy 2FA tokens into Ente Auth +--- + +# Migrating from Authy + +A guide written by Green, an ente.io lover + +> [!WARNING] +> +> Authy will soon be dropping support for its desktop apps in the near future. +> If you are looking to switch to ente Authenticator from Authy, I heavily +> recommend you export your codes as soon as you can. + +--- + +Migrating from Authy can be tiring, as you cannot export your 2FA codes through +the app, meaning that you would have to reconfigure 2FA for all of your accounts +for your new 2FA authenticator. However, easier ways exist to export your codes +out of Authy. This guide will cover two of the most used methods for mograting +from Authy to ente Authenticator. + +> [!CAUTION] +> +> Under any circumstances, do **NOT** share any JSON and TXT files generated +> using this guide, as they contain your **unencrypted** TOTP secrets! +> +> Also, there is **NO GUARANTEE** that these methods will export ALL of your +> codes. Make sure that all your accounts have been imported successfully before +> deleting any codes from your Authy account! + +--- + +## Method 1: Use Neeraj's export tool + +**Who should use this?** General users who want to save time by skipping the +hard (and rather technical) parts of the process.

+ +One way to export is to +[use this tool by Neeraj](https://github.com/ua741/authy-export/releases/tag/v0.0.4) +to simplify the process and skip directly to importing to ente Authenticator. + +To export from Authy, download the tool for your specific OS, then type the +following in your terminal: + +``` +./ +``` + +Assuming the filename of the binary remains unmodified and the working directory +of the terminal is the location of the binary, you should type this for MacOS: + +> [!NOTE] +> +> On Apple Silicon devices, Rosetta 2 may be required to run the binary. + +``` +./authy-export-darwin-amd64 authy_codes.txt +``` + +For Linux: + +``` +./authy-export-linux-amd64 authy_codes.txt +``` + +For Windows: + +``` +./authy-export-windows-amd64.exe authy_codes.txt +``` + +This will generate a text file called `authy_codes.txt`, which contains your +Authy codes in ente's plaintext export format. You can now import this to ente +Authenticator! + +## Method 2: Use gboudreau's GitHub guide + +**Who should use this?** Power users who have spare time on their hands and +prefer a more "known and established" solution to exporting Authy codes.

+ +A user on GitHub (gboudreau) wrote a guide to export codes from Authy (morpheus +on Discord found this and showed it to us), so we are going to be using that for +the migration. + +To export your data, please follow +[this guide](https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93). + +This will create a JSON file called `authy-to-bitwarden-export.json`, which +contains your Authy codes in Bitwarden's export format. You can now import this +to ente Authenticator! + +### Method 2.1: If the export worked, but the import didn't + +> [!NOTE] +> +> This is intended only for users who successfully exported their codes using the +> guide in method 2, but could not import it to ente Authenticator for whatever +> reason. If the import was successful, or you haven't tried to import the codes +> yet, ignore this section. +> +> If the export itself failed, try using +> [**method 1**](#method-1-use-neerajs-export-tool) instead. + +Usually, you should be able to import Bitwarden exports directly into ente +Authenticator. In case this didn't work for whatever reason, I've written a +program in Python that converts the JSON file into a TXT file that ente +Authenticator can use, so you can try importing using plain text import instead. + +You can download my program +[here](https://github.com/gweeeen/ducky/blob/main/duckys_other_stuff/authy_to_ente.py), +or you can copy the program below: + +```py +import json +import os + +totp = [] + +accounts = json.load(open('authy-to-bitwarden-export.json','r',encoding='utf-8')) + +for account in accounts['items']: + totp.append(account['login']['totp']+'\n') + +writer = open('auth_codes.txt','w+',encoding='utf-8') +writer.writelines(totp) +writer.close() + +print('Saved to ' + os.getcwd() + '/auth_codes.txt') +``` + +To convert the file with this program, you will need to install +[Python](https://www.python.org/downloads/) on your computer. + +Before you run the program, make sure that both the Python program and the JSON +file are in the same directory, otherwise this will not work! + +To run the Python program, open it in your IDE and run the program, or open your +terminal and type `python3 authy_to_ente.py` (MacOS/Linux, or any other OS that +uses bash) or `py -3 authy_to_ente.py` (Windows). Once you run it, a new TXT +file called `auth_codes.txt` will be generated. You can now import your data to +ente Authenticator! + +--- + +You should now have a TXT file (method 1, method 2.1) or a JSON file (method 2) +that countains your TOTP secrets, which can now be imported into ente +Authenticator. To import your codes, please follow one of the steps below, +depending on which method you used to export your codes. + +## Importing to ente Authenticator (Method 1, method 2.1) + +1. Copy the TXT file to one of your devices with ente Authenticator. +2. Log in to your account (if you haven't already), or press "Use without + backups". +3. Open the navigation menu (hamburger button on the top left), then press + "Data", then press "Import codes". +4. Select the "Plain text" option. +5. Select the TXT file that was made earlier. + +## Importing to ente Authenticator (Method 2) + +1. Copy the JSON file to one of your devices with ente Authenticator. +2. Log in to your account (if you haven't already), or press "Use without + backups". +3. Open the navigation menu (hamburger button on the top left), then press + "Data", then press "Import codes". +4. Select the "Bitwarden" option. +5. Select the JSON file that was made earlier. + +If this didn't work, refer to +[**method 2.1**](#method-21-if-the-export-worked-but-the-import-didnt).

+ +And that's it! You have now successfully migrated from Authy to ente +Authenticator. + +Now that your secrets are safely stored, I recommend you delete the unencrypted +JSON and TXT files that were made during the migration process for security. diff --git a/docs/docs/auth/migration-guides/export.md b/docs/docs/auth/migration-guides/export.md new file mode 100644 index 000000000..a66bea7b6 --- /dev/null +++ b/docs/docs/auth/migration-guides/export.md @@ -0,0 +1,76 @@ +--- +title: Exporting your data from Ente Auth +description: Guide for exporting your 2FA codes out from Ente Auth +--- + +# Exporting your data out of Ente Auth + +## Auth Encrypted Export format + +### Overview + +When we export the auth codes, the data is encrypted using a key derived from +the user's password. This document describes the JSON structure used to organize +exported data, including versioning and key derivation parameters. + +### Export JSON Sample + +```json +{ + "version": 1, + "kdfParams": { + "memLimit": 4096, + "opsLimit": 3, + "salt": "example_salt" + }, + "encryptedData": "encrypted_data_here", + "encryptionNonce": "nonce_here" +} +``` + +The main object used to represent the export data. It contains the following +key-value pairs: + +- `version`: The version of the export format. +- `kdfParams`: Key derivation function parameters. +- `encryptedData"`: The encrypted authentication data. +- `encryptionNonce`: The nonce used for encryption. + +#### Version + +Export version is used to identify the format of the export data. + +##### Ver: 1 + +- KDF Algorithm: `ARGON2ID` +- Decrypted data format: `otpauth://totp/...`, separated by a new line. +- Encryption Algo: `XChaCha20-Poly1305` + +##### Key Derivation Function Params (KDF) + +This section contains the parameters that were using during KDF operation: + +- `memLimit`: Memory limit for the algorithm. +- `opsLimit`: Operations limit for the algorithm. +- `salt`: The salt used in the derivation process. + +##### Encrypted Data + +As mentioned above, the auth data is encrypted using a key that's derived by +using user provided password & kdf params. For encryption, we are using +`XChaCha20-Poly1305` algorithm. + +## How to use the exported data + +- **Ente Authenticator app**: You can directly import the codes in the Ente + Authenticator app. + + > Settings -> Data -> Import Codes -> ente Encrypted export. + +- **Decrypt using Ente CLI** : Download the latest version of + [Ente CLI](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0), and run + the following command + +``` + ./ente auth decrypt +``` diff --git a/docs/docs/auth/migration-guides/index.md b/docs/docs/auth/migration-guides/index.md index 603c87011..f10d9db41 100644 --- a/docs/docs/auth/migration-guides/index.md +++ b/docs/docs/auth/migration-guides/index.md @@ -1,11 +1,10 @@ --- title: Migrating to Ente Auth description: - Guides for migrating your existing 2FA tokens from other products into Ente - Auth + Guides for migrating your existing 2FA tokens into or out of Ente Auth --- -# Migrating to Ente Auth +# Migrating to/from Ente Auth -_Coming soon_. This section will contain guides for migrating your existing 2FA -tokens from other products into Ente Auth. +- [Migrating from Authy](authy/) +- [Exporting your data out of Ente Auth](export) diff --git a/docs/docs/photos/faq/index.md b/docs/docs/photos/faq/index.md index 93eebcb56..c5a4e2cf9 100644 --- a/docs/docs/photos/faq/index.md +++ b/docs/docs/photos/faq/index.md @@ -3,9 +3,7 @@ title: FAQ description: Frequently asked questions about Ente Photos --- - # FAQ - -_Coming soon_. On this page we'll document some help items in a question and answer format. - +_Coming soon_. On this page we'll document some help items in a question and +answer format. diff --git a/docs/docs/self-hosting/faq/otp.md b/docs/docs/self-hosting/faq/otp.md index c3356b7bc..a224d1f66 100644 --- a/docs/docs/self-hosting/faq/otp.md +++ b/docs/docs/self-hosting/faq/otp.md @@ -17,3 +17,11 @@ locally by creating a `museum.yaml` and adding the `internal.hardcoded-ott` configuration setting to it. See [local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml) in the server source code for details about how to define this. + +> [!NOTE] +> +> If you're not able to get the OTP with the above methods, make sure that you +> are actually connecting to your self hosted instance and not to Ente's +> production servers. e.g. you can use the network requests tab in the browser +> console to verify that the API requests are going to your server instead of +> `api.ente.io`. diff --git a/docs/docs/self-hosting/faq/storage-space.md b/docs/docs/self-hosting/faq/storage-space.md new file mode 100644 index 000000000..f1ad78c71 --- /dev/null +++ b/docs/docs/self-hosting/faq/storage-space.md @@ -0,0 +1,12 @@ +--- +title: Increase storage space +description: Increasing the storage quota for users on your self hosted instance +--- + +# Increase storage space + +See the [guide for administering your server](/self-hosting/guides/admin). In +particular, you can use the `ente admin update-subscription` CLI command to +increase the +[storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md) +of accounts on your instance. diff --git a/docs/docs/self-hosting/guides/admin.md b/docs/docs/self-hosting/guides/admin.md new file mode 100644 index 000000000..91ea4d0f8 --- /dev/null +++ b/docs/docs/self-hosting/guides/admin.md @@ -0,0 +1,43 @@ +--- +title: Server admin +description: Administering your custom self-hosted Ente instance using the CLI +--- + +# Administering your custom server + +You can use +[Ente's CLI](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0) to +administer your self hosted server. + +First we need to get your CLI to connect to your custom server. Define a +config.yaml and put it either in the same directory as CLI or path defined in +env variable `ENTE_CLI_CONFIG_PATH` + +```yaml +endpoint: + api: "http://localhost:8080" +``` + +Now you should be able to +[add an account](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_add.md), +and subsequently increase the +[storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md) +using the CLI. + +For the admin actions, you can create `server/museum.yaml`, and whitelist add +the admin userID `internal.admins`. See +[local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml#L211C1-L232C1) +in the server source code for details about how to define this. + +```yaml +.... +internal: + admins: + # - 1580559962386440 + +.... +``` + +You can use +[account list](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_list.md) +command to find the user id of any account. diff --git a/docs/docs/self-hosting/guides/custom-server/index.md b/docs/docs/self-hosting/guides/custom-server/index.md index 51a9b6c2f..7f7ba50fa 100644 --- a/docs/docs/self-hosting/guides/custom-server/index.md +++ b/docs/docs/self-hosting/guides/custom-server/index.md @@ -1,9 +1,14 @@ --- title: Custom server -description: Using a custom self-hosted server with frontend apps +description: Using a custom self-hosted server with Ente client apps and CLI --- -# Custom server for mobile apps +# Connecting to a custom server + +You can modify various Ente client apps and CLI to connect to a self hosted +custom server endpoint. + +## Mobile apps The pre-built Ente apps from GitHub / App Store / Play Store / F-Droid can be easily configured to use a custom server. @@ -18,32 +23,17 @@ configure the endpoint the app should be connecting to. > This is only supported by the Ente Auth app currently. We'll add this same > functionality to the Ente Photos app soon. ---- +## CLI -# CLI +> [!NOTE] +> +> You can download the CLI from +> [here](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0) -> [!WARNING] -> The new version of CLI that supports connecting to custom server is still in beta. -> You can download the beta version from [here](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0&expanded=true) - -Define a config.yaml and put it either in the same directory as CLI or path defined in env variable `ENTE_CLI_CONFIG_PATH` +Define a config.yaml and put it either in the same directory as CLI or path +defined in env variable `ENTE_CLI_CONFIG_PATH` ```yaml endpoint: - api: "http://localhost:8080" + api: "http://localhost:8080" ``` - -You should be able to [add an account](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_add.md), and subsequently increase the [storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md) using the CLI. - -For the admin actions, you can create `server/museum.yaml`, and whitelist add the admin userID `internal.admins`. See [local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml#L211C1-L232C1) in the server source code for details about how to define this. - -You can use [account list](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_list.md) command to find the user id of any account. - -```yaml -.... -internal: - admins: - # - 1580559962386440 - -.... -``` \ No newline at end of file diff --git a/docs/docs/self-hosting/guides/index.md b/docs/docs/self-hosting/guides/index.md index 2f7582e31..a8a64d960 100644 --- a/docs/docs/self-hosting/guides/index.md +++ b/docs/docs/self-hosting/guides/index.md @@ -10,9 +10,11 @@ walkthroughs, tutorials and other FAQ pages in this directory. See the sidebar for existing guides. In particular: -* If you're just looking to get started, see [configure custom - server](custom-server/). +- If you're just looking to get started, see + [configure custom server](custom-server/). -* For self hosting both the server and web app using external S3 buckets for - object storage, see [using external S3](external-s3). +- For various admin related tasks, e.g. increasing the storage quota on your + self hosted instance, see [administering your custom server](admin). +- For self hosting both the server and web app using external S3 buckets for + object storage, see [using external S3](external-s3). diff --git a/docs/docs/self-hosting/guides/mobile-build.md b/docs/docs/self-hosting/guides/mobile-build.md index 32bba3aa7..c36903a20 100644 --- a/docs/docs/self-hosting/guides/mobile-build.md +++ b/docs/docs/self-hosting/guides/mobile-build.md @@ -30,8 +30,6 @@ flutter run --dart-define=endpoint=http://localhost:8080 --flavor independent -- flutter run --dart-define=endpoint=http://localhost:8080 ``` - - Or for the auth app: ```sh @@ -46,9 +44,11 @@ flutter run --dart-define=endpoint=http://localhost:8080 ``` ## How to build non-debug builds -For building APK, [setup your - keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore) - and run -```sh + +For building APK, +[setup your keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore) +and run + +```sh flutter build apk --release --flavor independent -t lib/main.dart - ``` +``` diff --git a/mobile/.gitignore b/mobile/.gitignore index 47e6ea41b..b6ee5c616 100644 --- a/mobile/.gitignore +++ b/mobile/.gitignore @@ -9,6 +9,9 @@ .history .svn/ +# Editors +.vscode/ + # IntelliJ related *.iml *.ipr @@ -25,7 +28,6 @@ .pub/ /build/ - # Web related lib/generated_plugin_registrant.dart @@ -37,6 +39,5 @@ android/app/.settings/* android/.settings/ .env - fastlane/report.xml TensorFlowLiteC.framework diff --git a/mobile/README.md b/mobile/README.md index f9383ce77..114a3ab38 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -45,8 +45,7 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. [Install Flutter v3.13.4](https://flutter.dev/docs/get-started/install) or - set the Path of Flutter SDK to `thirdparty/flutter/bin`. +1. [Install Flutter v3.13.4](https://flutter.dev/docs/get-started/install). 2. Pull in all submodules with `git submodule update --init --recursive` diff --git a/mobile/docs/release.md b/mobile/docs/release.md new file mode 100644 index 000000000..c7b4f5081 --- /dev/null +++ b/mobile/docs/release.md @@ -0,0 +1,32 @@ +# Releases + +Create a PR to bump up the version in `pubspec.yaml`. + +> [!NOTE] +> +> Use [semver](https://semver.org/) for the tags, with `photos-` as a prefix. +> Multiple beta releases for the same upcoming version can be done by adding +> build metadata at the end, e.g. `photos-v1.2.3-beta+3`. + +Once that is merged, tag main, and push the tag. + +```sh +git tag photos-v1.2.3 +git push origin photos-v1.2.3 +``` + +This'll trigger a GitHub workflow that: + +* Creates a new draft GitHub release and attaches the build artifacts to it + (mobile APKs), + +Once the workflow completes, go to the draft GitHub release that was created. + +> [!NOTE] +> +> Keep the title of the release same as the tag. + +Set "Previous tag" to the last release of auth and press "Generate release +notes". The generated release note will contain all PRs and new contributors +from all the releases in the monorepo, so you'll need to filter them to keep +only the things that relate to the Photos mobile app. diff --git a/mobile/docs/vscode/settings.json b/mobile/docs/vscode/settings.json deleted file mode 100644 index b6a9a3c02..000000000 --- a/mobile/docs/vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "dart.flutterSdkPath": "thirdparty/flutter/bin" -} diff --git a/mobile/fastlane/metadata/android/pt/full_description.txt b/mobile/fastlane/metadata/android/pt/full_description.txt index 6719156c6..2bd26df1e 100644 --- a/mobile/fastlane/metadata/android/pt/full_description.txt +++ b/mobile/fastlane/metadata/android/pt/full_description.txt @@ -1,6 +1,6 @@ ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. -Se você está procurando uma alternativa ao Google Photos com foco em privacidade, veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los. +Se você está procurando uma alternativa ao Google Fotos com foco em privacidade, você veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los. Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). diff --git a/mobile/fastlane/metadata/android/zh/full_description.txt b/mobile/fastlane/metadata/android/zh/full_description.txt index 6eddd122d..2410d9da0 100644 --- a/mobile/fastlane/metadata/android/zh/full_description.txt +++ b/mobile/fastlane/metadata/android/zh/full_description.txt @@ -1,12 +1,12 @@ ente 是一个简单的应用程序来备份和分享您的照片和视频。 -如果你一直在寻找一个隐私友好的Google Photos替代品,那么你就来对地方了。 使用 Ente,它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 +如果你一直在寻找一个隐私友好的Google Photos替代品,那么你就来对地方了。 使用 Ente,它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 使用 Ente,它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 我们在Android、iOS、web 和桌面上有开源应用, 和您的照片将以端到端加密方式 (e2ee) 无缝同步。 -ente也使分享相册给自己的爱人、亲人变得轻而易举,即使他们可能并不使用ente。 您可以分享可公开查看的链接,使他们可以查看您的相册,并通过添加照片来协作而不需要注册账户或下载app。 +ente也使分享相册给自己的爱人、亲人变得轻而易举,即使他们可能并不使用ente。 您可以分享可公开查看的链接,使他们可以查看您的相册,并通过添加照片来协作而不需要注册账户或下载app。 ente也使分享相册给自己的爱人、亲人变得轻而易举,即使他们可能并不使用ente。 您可以分享可公开查看的链接,使他们可以查看您的相册,并通过添加照片来协作而不需要注册账户或下载app。 权限 -您的加密数据已复制到三个不同的地点,包括巴黎的一个安全屋。 我们认真对待子孙后代,并确保您的回忆比您长寿。 +您的加密数据已复制到三个不同的地点,包括巴黎的一个安全屋。 我们认真对待子孙后代,并确保您的回忆比您长寿。 我们认真对待子孙后代,并确保您的回忆比您长寿。 我们来这里是为了打造有史以来最安全的照片应用,来和我们一起前行! @@ -30,7 +30,7 @@ ente也使分享相册给自己的爱人、亲人变得轻而易举,即使他 ente需要特定权限以执行作为图像存储提供商的职责,相关内容可以在此链接查阅:https://github.com/ente-io/photos-app/blob/f-droid/android/permissions.md 价格 -我们不会提供永久免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 +我们不会提供永久免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 支持 -我们对提供真人支持感到自豪。 如果您是我们的付费客户,您可以联系 team@ente.io 并在24小时内收到来自我们团队的回复。 +我们对提供真人支持感到自豪。 我们对提供真人支持感到自豪。 如果您是我们的付费客户,您可以联系 team@ente.io 并在24小时内收到来自我们团队的回复。 diff --git a/mobile/fastlane/metadata/ios/pt/description.txt b/mobile/fastlane/metadata/ios/pt/description.txt index c2c44ed89..3d948a2b2 100644 --- a/mobile/fastlane/metadata/ios/pt/description.txt +++ b/mobile/fastlane/metadata/ios/pt/description.txt @@ -1,6 +1,6 @@ Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. -Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los. +Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com Ente, elas são armazenadas com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-las. Temos aplicativos de código aberto em Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). diff --git a/mobile/fastlane/metadata/playstore/pt/full_description.txt b/mobile/fastlane/metadata/playstore/pt/full_description.txt index 18fa7675f..fc02bad10 100644 --- a/mobile/fastlane/metadata/playstore/pt/full_description.txt +++ b/mobile/fastlane/metadata/playstore/pt/full_description.txt @@ -1,6 +1,6 @@ Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. -Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com ente, eles são armazenados com criptografados de ponta a ponta (e2ee). Isso significa que só você pode vê-los. +Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com Ente, elas são armazenadas com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-las. Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). diff --git a/mobile/lib/face/db_model_mappers.dart b/mobile/lib/face/db_model_mappers.dart index 57dc311aa..be9596ffd 100644 --- a/mobile/lib/face/db_model_mappers.dart +++ b/mobile/lib/face/db_model_mappers.dart @@ -25,17 +25,6 @@ bool sqlIntToBool(int? value, {bool defaultValue = false}) { } } -Map mapToFaceDB(PersonFace personFace) { - return { - faceIDColumn: personFace.face.faceID, - faceDetectionColumn: json.encode(personFace.face.detection.toJson()), - faceConfirmedColumn: boolToSQLInt(personFace.confirmed), - faceClusterId: personFace.personID, - faceClosestDistColumn: personFace.closeDist, - faceClosestFaceID: personFace.closeFaceID, - }; -} - Map mapPersonToRow(Person p) { return { idColumn: p.remoteID, diff --git a/mobile/lib/face/feedback.dart b/mobile/lib/face/feedback.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/mobile/lib/face/model/person_face.dart b/mobile/lib/face/model/person_face.dart deleted file mode 100644 index 6d9744c27..000000000 --- a/mobile/lib/face/model/person_face.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:photos/face/model/face.dart'; - -class PersonFace { - final Face face; - int? personID; - bool? confirmed; - double? closeDist; - String? closeFaceID; - - PersonFace( - this.face, - this.personID, - this.closeDist, - this.closeFaceID, { - this.confirmed, - }); - - // toJson - Map toJson() => { - 'face': face.toJson(), - 'personID': personID, - 'confirmed': confirmed ?? false, - 'close_dist': closeDist, - 'close_face_id': closeFaceID, - }; - - // fromJson - factory PersonFace.fromJson(Map json) { - return PersonFace( - Face.fromJson(json['face'] as Map), - json['personID'] as int?, - json['close_dist'] as double?, - json['close_face_id'] as String?, - confirmed: json['confirmed'] as bool?, - ); - } -} diff --git a/mobile/lib/face/utils/import_from_zip.dart b/mobile/lib/face/utils/import_from_zip.dart deleted file mode 100644 index e4dd19d31..000000000 --- a/mobile/lib/face/utils/import_from_zip.dart +++ /dev/null @@ -1,44 +0,0 @@ -// import "dart:io"; - -import "package:dio/dio.dart"; -import "package:logging/logging.dart"; -import "package:photos/core/configuration.dart"; -import "package:photos/core/network/network.dart"; -import "package:photos/face/model/face.dart"; - -final _logger = Logger("import_from_zip"); -Future> downloadZip() async { - final List result = []; - for (int i = 0; i < 2; i++) { - _logger.info("downloading $i"); - final remoteZipUrl = "http://192.168.1.13:8700/ml/cx_ml_json_${i}.json"; - final response = await NetworkClient.instance.getDio().get( - remoteZipUrl, - options: Options( - headers: {"X-Auth-Token": Configuration.instance.getToken()}, - ), - ); - - if (response.statusCode != 200) { - _logger.warning('download failed ${response.toString()}'); - throw Exception("download failed"); - } - final res = response.data as List; - for (final item in res) { - try { - result.add(Face.fromJson(item)); - } catch (e) { - _logger.warning("failed to parse $item"); - rethrow; - } - } - } - final Set faceID = {}; - for (final face in result) { - if (faceID.contains(face.faceID)) { - _logger.warning("duplicate faceID ${face.faceID}"); - } - faceID.add(face.faceID); - } - return result; -} diff --git a/mobile/lib/l10n/intl_de.arb b/mobile/lib/l10n/intl_de.arb index 08677bbd2..9604c03d8 100644 --- a/mobile/lib/l10n/intl_de.arb +++ b/mobile/lib/l10n/intl_de.arb @@ -406,6 +406,15 @@ }, "photoGridSize": "Fotorastergröße", "manageDeviceStorage": "Gerätespeicher verwalten", + "machineLearning": "Maschinelles Lernen", + "magicSearch": "Magische Suche", + "magicSearchDescription": "Bitte beachten Sie, dass dies mehr Bandbreite nutzt und zu einem höheren Akkuverbrauch führt, bis alle Elemente indiziert sind.", + "loadingModel": "Lade Modelle herunter...", + "waitingForWifi": "Warte auf WLAN...", + "status": "Status", + "indexedItems": "Indizierte Elemente", + "pendingItems": "Ausstehende Elemente", + "clearIndexes": "Indexe löschen", "selectFoldersForBackup": "Ordner für Sicherung auswählen", "selectedFoldersWillBeEncryptedAndBackedUp": "Ausgewählte Ordner werden verschlüsselt und gesichert", "unselectAll": "Alle demarkieren", @@ -1178,9 +1187,19 @@ "changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?", "editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein", "cleanUncategorized": "Unkategorisiert leeren", - "joinDiscord": "Join Discord", - "locations": "Locations", - "descriptions": "Descriptions", "addAName": "Add a name", - "findPeopleByName": "Find people quickly by searching by name" + "findPeopleByName": "Find people quickly by searching by name", + "cleanUncategorizedDescription": "Entferne alle Dateien von \"Unkategorisiert\" die in anderen Alben vorhanden sind", + "waitingForVerification": "Warte auf Bestätigung...", + "passkey": "Passkey", + "passkeyAuthTitle": "Passkey-Verifizierung", + "verifyPasskey": "Passkey verifizieren", + "playOnTv": "Album auf dem Fernseher wiedergeben", + "pair": "Koppeln", + "deviceNotFound": "Gerät nicht gefunden", + "castInstruction": "Besuche cast.ente.io auf dem Gerät, das du verbinden möchtest.\n\nGib den unten angegebenen Code ein, um das Album auf deinem Fernseher abzuspielen.", + "deviceCodeHint": "Code eingeben", + "joinDiscord": "Discord beitreten", + "locations": "Orte", + "descriptions": "Beschreibungen" } \ No newline at end of file diff --git a/mobile/lib/l10n/intl_pt.arb b/mobile/lib/l10n/intl_pt.arb index 8a9ef5cb3..9a842fbe9 100644 --- a/mobile/lib/l10n/intl_pt.arb +++ b/mobile/lib/l10n/intl_pt.arb @@ -1187,18 +1187,18 @@ "changeLocationOfSelectedItems": "Alterar o local dos itens selecionados?", "editsToLocationWillOnlyBeSeenWithinEnte": "Edições para local só serão vistas dentro do Ente", "cleanUncategorized": "Limpar Sem Categoria", - "waitingForBrowserRequest": "Aguardando solicitação do navegador...", - "launchPasskeyUrlAgain": "Iniciar a URL de chave de acesso novamente", + "cleanUncategorizedDescription": "Remover todos os arquivos de Não Categorizados que estão presentes em outros álbuns", + "waitingForVerification": "Esperando por verificação...", "passkey": "Chave de acesso", "passkeyAuthTitle": "Autenticação via Chave de acesso", + "verifyPasskey": "Verificar chave de acesso", "playOnTv": "Reproduzir álbum na TV", "pair": "Parear", "deviceNotFound": "Dispositivo não encontrado", "castInstruction": "Visite cast.ente.io no dispositivo que você deseja parear.\n\ndigite o código abaixo para reproduzir o álbum em sua TV.", "deviceCodeHint": "Insira o código", "joinDiscord": "Junte-se ao Discord", - "locations": "Locations", - "descriptions": "Descriptions", - "addAName": "Add a name", - "findPeopleByName": "Find people quickly by searching by name" + "findPeopleByName": "Find people quickly by searching by name", + "locations": "Locais", + "descriptions": "Descrições" } \ No newline at end of file diff --git a/mobile/lib/l10n/intl_zh.arb b/mobile/lib/l10n/intl_zh.arb index 83190df5a..64b3e957f 100644 --- a/mobile/lib/l10n/intl_zh.arb +++ b/mobile/lib/l10n/intl_zh.arb @@ -1187,18 +1187,17 @@ "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?", "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到", "cleanUncategorized": "清除未分类的", - "waitingForBrowserRequest": "正在等待浏览器请求...", - "launchPasskeyUrlAgain": "再次启动 通行密钥 URL", + "cleanUncategorizedDescription": "从“未分类”中删除其他相册中存在的所有文件", + "waitingForVerification": "等待验证...", "passkey": "通行密钥", "passkeyAuthTitle": "通行密钥认证", + "verifyPasskey": "验证通行密钥", "playOnTv": "在电视上播放相册", "pair": "配对", "deviceNotFound": "未发现设备", "castInstruction": "在您要配对的设备上访问 cast.ente.io。\n输入下面的代码即可在电视上播放相册。", "deviceCodeHint": "输入代码", "joinDiscord": "加入 Discord", - "locations": "Locations", - "descriptions": "Descriptions", "addAName": "Add a name", "findPeopleByName": "Find people quickly by searching by name" } \ No newline at end of file diff --git a/mobile/lib/ui/viewer/gallery/component/gallery_file_widget.dart b/mobile/lib/ui/viewer/gallery/component/gallery_file_widget.dart index c6b89de0b..5f1ce7cbf 100644 --- a/mobile/lib/ui/viewer/gallery/component/gallery_file_widget.dart +++ b/mobile/lib/ui/viewer/gallery/component/gallery_file_widget.dart @@ -68,6 +68,7 @@ class GalleryFileWidget extends StatelessWidget { : _onLongPressNoSelectionLimit(context, file); }, child: Stack( + clipBehavior: Clip.none, children: [ ClipRRect( borderRadius: BorderRadius.circular(1), diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index c785cfc37..7382164b5 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.8.68+588 +version: 0.8.71+591 publish_to: none environment: diff --git a/mobile/thirdparty/transistor-background-fetch/.gitignore b/mobile/thirdparty/transistor-background-fetch/.gitignore deleted file mode 100644 index 296d68a8f..000000000 --- a/mobile/thirdparty/transistor-background-fetch/.gitignore +++ /dev/null @@ -1,63 +0,0 @@ -# Eclipse -.metadata - -# Xcode -# -.DS_Store -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control -# -#Pods/ - -# Eclipse - -# built application files -*.apk -*.ap_ - -# files for the dex VM -*.dex - -# Java class files -*.class - -# generated files -bin/ -gen/ - -# Local configuration file (sdk path, etc) -local.properties - -# Eclipse project files -.classpath -.project - -# Proguard folder generated by Eclipse -proguard/ - -# Intellij project files -*.iml -*.ipr -*.iws -.idea/ - diff --git a/mobile/thirdparty/transistor-background-fetch/LICENSE b/mobile/thirdparty/transistor-background-fetch/LICENSE deleted file mode 100644 index 526bbac98..000000000 --- a/mobile/thirdparty/transistor-background-fetch/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017 Transistor Software - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/mobile/thirdparty/transistor-background-fetch/README.md b/mobile/thirdparty/transistor-background-fetch/README.md deleted file mode 100644 index 418370978..000000000 --- a/mobile/thirdparty/transistor-background-fetch/README.md +++ /dev/null @@ -1,22 +0,0 @@ -Transistor Background Fetch -=========================================================================== - -Copyright (c) 2017 Transistor Software - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/mobile/thirdparty/transistor-background-fetch/TSBackgroundFetch.podspec b/mobile/thirdparty/transistor-background-fetch/TSBackgroundFetch.podspec deleted file mode 100644 index 1967bd2e9..000000000 --- a/mobile/thirdparty/transistor-background-fetch/TSBackgroundFetch.podspec +++ /dev/null @@ -1,28 +0,0 @@ -# -# Be sure to run `pod lib lint TSBackgroundFetch.podspec' to ensure this is a -# valid spec before submitting. -# -# Any lines starting with a # are optional, but their use is encouraged -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# - -Pod::Spec.new do |s| - s.name = 'TSBackgroundFetch' - s.version = '0.0.1' - s.summary = 'iOS Background Fetch API Manager' - - s.description = <<-DESC -iOS Background Fetch API Manager with ability to handle multiple listeners. - DESC - - s.homepage = 'http://www.transistorsoft.com' - s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'christocracy' => 'christocracy@gmail.com' } - s.source = { :git => 'https://github.com/transistorsoft/transistor-background-fetch.git', :tag => s.version.to_s } - s.social_media_url = 'https://twitter.com/christocracy' - - s.ios.deployment_target = '8.0' - - s.source_files = 'ios/TSBackgroundFetch/TSBackgroundFetch/*.{h,m}' - s.vendored_frameworks = 'ios/TSBackgroundFetch/TSBackgroundFetch.framework' -end diff --git a/mobile/thirdparty/transistor-background-fetch/android/.gitignore b/mobile/thirdparty/transistor-background-fetch/android/.gitignore deleted file mode 100644 index 39fb081a4..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.externalNativeBuild diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/.gitignore b/mobile/thirdparty/transistor-background-fetch/android/app/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/build.gradle b/mobile/thirdparty/transistor-background-fetch/android/app/build.gradle deleted file mode 100644 index 93a4040d2..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 26 - defaultConfig { - applicationId "com.transistorsoft.backgroundfetch" - minSdkVersion 16 - targetSdkVersion 26 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:26.1.0' - -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/proguard-rules.pro b/mobile/thirdparty/transistor-background-fetch/android/app/proguard-rules.pro deleted file mode 100644 index f1b424510..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/androidTest/java/com/transistorsoft/backgroundfetch/ExampleInstrumentedTest.java b/mobile/thirdparty/transistor-background-fetch/android/app/src/androidTest/java/com/transistorsoft/backgroundfetch/ExampleInstrumentedTest.java deleted file mode 100644 index 983348763..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/androidTest/java/com/transistorsoft/backgroundfetch/ExampleInstrumentedTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.transistorsoft.backgroundfetch; - -import android.content.Context; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.transistorsoft.backgroundfetch", appContext.getPackageName()); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/AndroidManifest.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index b22ee9b63..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index c7bd21dbd..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable/ic_launcher_background.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index d5fccc538..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cfe5..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cfe5..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a2f590828..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 1b5239980..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ff10afd6e..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 115a4c768..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index dcd3cd808..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 459ca609d..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 8ca12fe02..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8e19b410a..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b824ebdd4..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 4c19a13c2..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/colors.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/colors.xml deleted file mode 100644 index 3ab3e9cbc..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #3F51B5 - #303F9F - #FF4081 - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/strings.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 89ece41f6..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - BackgroundFetch - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/styles.xml b/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 5885930df..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/mobile/thirdparty/transistor-background-fetch/android/app/src/test/java/com/transistorsoft/backgroundfetch/ExampleUnitTest.java b/mobile/thirdparty/transistor-background-fetch/android/app/src/test/java/com/transistorsoft/backgroundfetch/ExampleUnitTest.java deleted file mode 100644 index 6536d1cd3..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/app/src/test/java/com/transistorsoft/backgroundfetch/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.transistorsoft.backgroundfetch; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/mobile/thirdparty/transistor-background-fetch/android/build.gradle b/mobile/thirdparty/transistor-background-fetch/android/build.gradle deleted file mode 100644 index c46c347b1..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} - -ext { - compileSdkVersion = 29 - targetSdkVersion = 29 - buildToolsVersion = "29.0.6" - appCompatVersion = "1.1.0" -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/gradle.properties b/mobile/thirdparty/transistor-background-fetch/android/gradle.properties deleted file mode 100644 index 85104bf95..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/gradle.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -VERSION_NAME=0.5.0 -VERSION_CODE=15 - -android.useAndroidX=true -android.enableJetifier=true diff --git a/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.jar b/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 13372aef5..000000000 Binary files a/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.properties b/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 7a8c57bad..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -#Thu Feb 09 18:40:48 IST 2023 -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -distributionSha256Sum=10065868c78f1207afb3a92176f99a37d753a513dff453abb6b5cceda4058cda diff --git a/mobile/thirdparty/transistor-background-fetch/android/gradlew b/mobile/thirdparty/transistor-background-fetch/android/gradlew deleted file mode 100755 index 9d82f7891..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/gradlew +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/mobile/thirdparty/transistor-background-fetch/android/gradlew.bat b/mobile/thirdparty/transistor-background-fetch/android/gradlew.bat deleted file mode 100644 index 8a0b282aa..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/mobile/thirdparty/transistor-background-fetch/android/settings.gradle b/mobile/thirdparty/transistor-background-fetch/android/settings.gradle deleted file mode 100644 index 8b053e2cc..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app', ':tsbackgroundfetch' diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/.gitignore b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/build.gradle b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/build.gradle deleted file mode 100644 index d64e6cfdd..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/build.gradle +++ /dev/null @@ -1,136 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'maven' -apply plugin: 'maven-publish' - -android { - compileSdkVersion rootProject.compileSdkVersion - defaultConfig { - minSdkVersion 16 - targetSdkVersion rootProject.targetSdkVersion - versionCode 1 - versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - publishing { - publications { - tslocationmanager(MavenPublication) { - groupId 'com.transistorsoft' - artifactId 'tsbackgroundfetch' - version VERSION_NAME - artifact("$buildDir/outputs/aar/tsbackgroundfetch-release.aar") - - } - } - repositories { - mavenLocal() - } - } -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - - implementation fileTree(dir: 'libs', include: ['*.jar']) - - //implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion" - -} - -// Build Release -task buildRelease { task -> - task.dependsOn 'cordovaRelease' - task.dependsOn 'reactNativeRelease' - task.dependsOn 'nativeScriptRelease' - task.dependsOn 'flutterRelease' -} - -// Publish Release. -task publishRelease { task -> - task.dependsOn 'assembleRelease' -} -tasks["publishRelease"].mustRunAfter("assembleRelease") -tasks["publishRelease"].finalizedBy("publish") - -def WORKSPACE_PATH = "/Volumes/Glyph2TB/Users/chris/workspace" - -// Build local maven repo. -def LIBRARY_PATH = "com/transistorsoft/tsbackgroundfetch" -task buildLocalRepository { task -> - task.dependsOn 'publishRelease' - doLast { - delete "$buildDir/repo-local" - copy { - from "$buildDir/repo/$LIBRARY_PATH/$VERSION_NAME" - into "$buildDir/repo-local/$LIBRARY_PATH/$VERSION_NAME" - } - copy { - from("$buildDir/repo/$LIBRARY_PATH/maven-metadata.xml") - into("$buildDir/repo-local/$LIBRARY_PATH") - } - } -} - -def cordovaDir = "$WORKSPACE_PATH/cordova/background-geolocation/cordova-plugin-background-fetch" -task cordovaRelease { task -> - task.dependsOn 'buildLocalRepository' - doLast { - delete "$cordovaDir/src/android/libs" - copy { - // Maven repo format. - from("$buildDir/repo-local") - into("$cordovaDir/src/android/libs") - // OLD FORMAT - //from("$buildDir/outputs/aar/tsbackgroundfetch-release.aar") - //into("$cordovaDir/src/android/libs/tsbackgroundfetch") - //rename(/(.*)-release/, '$1-' + VERSION_NAME) - } - } -} - -def reactNativeDir = "$WORKSPACE_PATH/react/background-geolocation/react-native-background-fetch" -task reactNativeRelease { task -> - task.dependsOn 'buildLocalRepository' - doLast { - delete "$reactNativeDir/android/libs" - copy { - // Maven repo format. - from("$buildDir/repo-local") - into("$reactNativeDir/android/libs") - // OLD format. - //from("$buildDir/outputs/aar/tsbackgroundfetch-release.aar") - //into("$reactNativeDir/android/libs") - //rename(/(.*)-release/, '$1-' + VERSION_NAME) - } - } -} - -def flutterDir = "$WORKSPACE_PATH/background-geolocation/flutter/flutter_background_fetch" -task flutterRelease { task -> - task.dependsOn 'buildLocalRepository' - doLast { - delete "$flutterDir/android/libs" - copy { - // Maven repo format. - from("$buildDir/repo-local") - into("$flutterDir/android/libs") - // OLD format. - //from("$buildDir/outputs/aar/tsbackgroundfetch-release.aar") - //into("$flutterDir/android/libs") - //rename(/(.*)-release/, '$1-' + VERSION_NAME) - } - } -} - -task nativeScriptRelease(type: Copy) { - from('./build/outputs/aar/tsbackgroundfetch-release.aar') - into("$WORKSPACE_PATH/NativeScript/background-geolocation/nativescript-background-fetch/src/platforms/android/libs") - rename('tsbackgroundfetch-release.aar', 'tsbackgroundfetch.aar') -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/proguard-rules.pro b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/proguard-rules.pro deleted file mode 100644 index f1b424510..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/androidTest/java/com/transistorsoft/tsbackgroundfetch/ExampleInstrumentedTest.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/androidTest/java/com/transistorsoft/tsbackgroundfetch/ExampleInstrumentedTest.java deleted file mode 100644 index 2bb391be3..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/androidTest/java/com/transistorsoft/tsbackgroundfetch/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.transistorsoft.tsbackgroundfetch.test", appContext.getPackageName()); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/AndroidManifest.xml b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/AndroidManifest.xml deleted file mode 100644 index 1cd70cd57..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BGTask.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BGTask.java deleted file mode 100644 index a858dcf59..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BGTask.java +++ /dev/null @@ -1,279 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.job.JobInfo; -import android.app.job.JobScheduler; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.PersistableBundle; -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -public class BGTask { - static int MAX_TIME = 60000; - - private static final List mTasks = new ArrayList<>(); - - static BGTask getTask(String taskId) { - synchronized (mTasks) { - for (BGTask task : mTasks) { - if (task.hasTaskId(taskId)) return task; - } - } - return null; - } - - static void addTask(BGTask task) { - synchronized (mTasks) { - mTasks.add(task); - } - } - - static void removeTask(String taskId) { - synchronized (mTasks) { - BGTask found = null; - for (BGTask task : mTasks) { - if (task.hasTaskId(taskId)) { - found = task; - break; - } - } - if (found != null) { - mTasks.remove(found); - } - } - } - - static void clear() { - synchronized (mTasks) { - mTasks.clear(); - } - } - - private FetchJobService.CompletionHandler mCompletionHandler; - private String mTaskId; - private int mJobId; - private Runnable mTimeoutTask; - private boolean mTimedout = false; - - BGTask(final Context context, String taskId, FetchJobService.CompletionHandler handler, int jobId) { - mTaskId = taskId; - mCompletionHandler = handler; - mJobId = jobId; - - mTimeoutTask = new Runnable() { - @Override public void run() { - onTimeout(context); - } - }; - BackgroundFetch.getUiHandler().postDelayed(mTimeoutTask, MAX_TIME); - } - - public boolean getTimedOut() { - return mTimedout; - } - - public String getTaskId() { return mTaskId; } - - int getJobId() { return mJobId; } - - boolean hasTaskId(String taskId) { - return ((mTaskId != null) && mTaskId.equalsIgnoreCase(taskId)); - } - - void setCompletionHandler(FetchJobService.CompletionHandler handler) { - mCompletionHandler = handler; - } - - void finish() { - if (mCompletionHandler != null) { - mCompletionHandler.finish(); - } - if (mTimeoutTask != null) { - BackgroundFetch.getUiHandler().removeCallbacks(mTimeoutTask); - } - mCompletionHandler = null; - removeTask(mTaskId); - } - - static void schedule(Context context, BackgroundFetchConfig config) { - Log.d(BackgroundFetch.TAG, config.toString()); - - long interval = (config.isFetchTask()) ? (TimeUnit.MINUTES.toMillis(config.getMinimumFetchInterval())) : config.getDelay(); - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !config.getForceAlarmManager()) { - // API 21+ uses new JobScheduler API - - JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - JobInfo.Builder builder = new JobInfo.Builder(config.getJobId(), new ComponentName(context, FetchJobService.class)) - .setRequiredNetworkType(config.getRequiredNetworkType()) - .setRequiresDeviceIdle(config.getRequiresDeviceIdle()) - .setRequiresCharging(config.getRequiresCharging()) - .setPersisted(config.getStartOnBoot() && !config.getStopOnTerminate()); - - if (config.getPeriodic()) { - if (android.os.Build.VERSION.SDK_INT >= 24) { - builder.setPeriodic(interval, interval); - } else { - builder.setPeriodic(interval); - } - } else { - builder.setMinimumLatency(interval); - } - PersistableBundle extras = new PersistableBundle(); - extras.putString(BackgroundFetchConfig.FIELD_TASK_ID, config.getTaskId()); - builder.setExtras(extras); - - if (android.os.Build.VERSION.SDK_INT >= 26) { - builder.setRequiresStorageNotLow(config.getRequiresStorageNotLow()); - builder.setRequiresBatteryNotLow(config.getRequiresBatteryNotLow()); - } - if (jobScheduler != null) { - jobScheduler.schedule(builder.build()); - } - } else { - // Everyone else get AlarmManager - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - if (alarmManager != null) { - PendingIntent pi = getAlarmPI(context, config.getTaskId()); - long delay = System.currentTimeMillis() + interval; - if (config.getPeriodic()) { - alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, delay, interval, pi); - } else { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, delay, pi); - } else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, delay, pi); - } else { - alarmManager.set(AlarmManager.RTC_WAKEUP, delay, pi); - } - } - } - } - } - - void onTimeout(Context context) { - mTimedout = true; - Log.d(BackgroundFetch.TAG, "[BGTask] timeout: " + mTaskId); - - BackgroundFetch adapter = BackgroundFetch.getInstance(context); - - if (adapter.isMainActivityActive()) { - BackgroundFetch.Callback callback = adapter.getFetchCallback(); - if (callback != null) { - callback.onTimeout(mTaskId); - } - } else { - BackgroundFetchConfig config = adapter.getConfig(mTaskId); - if (config != null) { - if (config.getJobService() != null) { - fireHeadlessEvent(context, config); - } else { - adapter.finish(mTaskId); - } - } else { - Log.e(BackgroundFetch.TAG, "[BGTask] failed to load config for taskId: " + mTaskId); - adapter.finish(mTaskId); - } - } - } - - // Fire a headless background-fetch event by reflecting an instance of Config.jobServiceClass. - // Will attempt to reflect upon two different forms of Headless class: - // 1: new HeadlessTask(context, taskId) - // or - // 2: new HeadlessTask().onFetch(context, taskId); - // - void fireHeadlessEvent(Context context, BackgroundFetchConfig config) throws Error { - try { - // Get class via reflection. - Class HeadlessClass = Class.forName(config.getJobService()); - Class[] types = { Context.class, BGTask.class }; - Object[] params = { context, this}; - try { - // 1: new HeadlessTask(context, taskId); - Constructor constructor = HeadlessClass.getDeclaredConstructor(types); - constructor.newInstance(params); - } catch (NoSuchMethodException e) { - // 2: new HeadlessTask().onFetch(context, taskId); - Constructor constructor = HeadlessClass.getConstructor(); - Object instance = constructor.newInstance(); - Method onFetch = instance.getClass().getDeclaredMethod("onFetch", types); - onFetch.invoke(instance, params); - } - } catch (ClassNotFoundException e) { - throw new Error(e.getMessage()); - } catch (NoSuchMethodException e) { - throw new Error(e.getMessage()); - } catch (IllegalAccessException e) { - throw new Error(e.getMessage()); - } catch (InstantiationException e) { - throw new Error(e.getMessage()); - } catch (InvocationTargetException e) { - throw new Error(e.getMessage()); - } - } - - static void cancel(Context context, String taskId, int jobId) { - Log.i(BackgroundFetch.TAG, "- cancel taskId=" + taskId + ", jobId=" + jobId); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && (jobId != 0)) { - JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - if (jobScheduler != null) { - jobScheduler.cancel(jobId); - } - } else { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - if (alarmManager != null) { - alarmManager.cancel(BGTask.getAlarmPI(context, taskId)); - } - } - } - - static PendingIntent getAlarmPI(Context context, String taskId) { - Intent intent = new Intent(context, FetchAlarmReceiver.class); - intent.setAction(taskId); - return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - } - - public String toString() { - return "[BGTask taskId=" + mTaskId + "]"; - } - - public Map toMap() { - Map map = new HashMap<>(); - map.put("taskId", mTaskId); - map.put("timeout", mTimedout); - return map; - } - - public JSONObject toJson() { - JSONObject json = new JSONObject(); - try { - json.put("taskId", mTaskId); - json.put("timeout", mTimedout); - } catch (JSONException e) { - e.printStackTrace(); - } - return json; - } - - static class Error extends RuntimeException { - public Error(String msg) { - super(msg); - } - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetch.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetch.java deleted file mode 100644 index 8c374c65c..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetch.java +++ /dev/null @@ -1,306 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.annotation.TargetApi; -import android.app.ActivityManager; - -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; - -import android.util.Log; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by chris on 2018-01-11. - */ - -public class BackgroundFetch { - public static final String TAG = "TSBackgroundFetch"; - - public static final String ACTION_CONFIGURE = "configure"; - public static final String ACTION_START = "start"; - public static final String ACTION_STOP = "stop"; - public static final String ACTION_FINISH = "finish"; - public static final String ACTION_STATUS = "status"; - public static final String ACTION_FORCE_RELOAD = TAG + "-forceReload"; - - public static final String EVENT_FETCH = ".event.BACKGROUND_FETCH"; - - public static final int STATUS_AVAILABLE = 2; - - private static BackgroundFetch mInstance = null; - - private static ExecutorService sThreadPool; - - private static Handler uiHandler; - - @SuppressWarnings({"WeakerAccess"}) - public static Handler getUiHandler() { - if (uiHandler == null) { - uiHandler = new Handler(Looper.getMainLooper()); - } - return uiHandler; - } - - @SuppressWarnings({"WeakerAccess"}) - public static ExecutorService getThreadPool() { - if (sThreadPool == null) { - sThreadPool = Executors.newCachedThreadPool(); - } - return sThreadPool; - } - - @SuppressWarnings({"WeakerAccess"}) - public static BackgroundFetch getInstance(Context context) { - if (mInstance == null) { - mInstance = getInstanceSynchronized(context.getApplicationContext()); - } - return mInstance; - } - - private static synchronized BackgroundFetch getInstanceSynchronized(Context context) { - if (mInstance == null) mInstance = new BackgroundFetch(context.getApplicationContext()); - return mInstance; - } - - private Context mContext; - private BackgroundFetch.Callback mFetchCallback; - - private final Map mConfig = new HashMap<>(); - - private BackgroundFetch(Context context) { - mContext = context; - } - - @SuppressWarnings({"unused"}) - public void configure(BackgroundFetchConfig config, BackgroundFetch.Callback callback) { - Log.d(TAG, "- " + ACTION_CONFIGURE); - mFetchCallback = callback; - - synchronized (mConfig) { - mConfig.put(config.getTaskId(), config); - } - start(config.getTaskId()); - } - - void onBoot() { - BackgroundFetchConfig.load(mContext, new BackgroundFetchConfig.OnLoadCallback() { - @Override public void onLoad(List result) { - for (BackgroundFetchConfig config : result) { - if (!config.getStartOnBoot() || config.getStopOnTerminate()) { - config.destroy(mContext); - continue; - } - synchronized (mConfig) { - mConfig.put(config.getTaskId(), config); - } - if ((android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) || config.getForceAlarmManager()) { - if (config.isFetchTask()) { - start(config.getTaskId()); - } else { - scheduleTask(config); - } - } - } - } - }); - } - - @SuppressWarnings({"WeakerAccess"}) - @TargetApi(21) - public void start(String fetchTaskId) { - Log.d(TAG, "- " + ACTION_START); - - BGTask task = BGTask.getTask(fetchTaskId); - if (task != null) { - Log.e(TAG, "[" + TAG + " start] Task " + fetchTaskId + " already registered"); - return; - } - registerTask(fetchTaskId); - } - - @SuppressWarnings({"WeakerAccess"}) - public void stop(String taskId) { - String msg = "- " + ACTION_STOP; - if (taskId != null) { - msg += ": " + taskId; - } - Log.d(TAG, msg); - - if (taskId == null) { - synchronized (mConfig) { - for (BackgroundFetchConfig config : mConfig.values()) { - BGTask task = BGTask.getTask(config.getTaskId()); - if (task != null) { - task.finish(); - BGTask.removeTask(config.getTaskId()); - } - BGTask.cancel(mContext, config.getTaskId(), config.getJobId()); - config.destroy(mContext); - } - BGTask.clear(); - } - } else { - BGTask task = BGTask.getTask(taskId); - if (task != null) { - task.finish(); - BGTask.removeTask(task.getTaskId()); - } - BackgroundFetchConfig config = getConfig(taskId); - if (config != null) { - config.destroy(mContext); - BGTask.cancel(mContext, config.getTaskId(), config.getJobId()); - } - } - } - - @SuppressWarnings({"WeakerAccess"}) - public void scheduleTask(BackgroundFetchConfig config) { - synchronized (mConfig) { - if (mConfig.containsKey(config.getTaskId())) { - // This BackgroundFetchConfig already exists? Should we halt any existing Job/Alarm here? - } - config.save(mContext); - mConfig.put(config.getTaskId(), config); - } - String taskId = config.getTaskId(); - registerTask(taskId); - } - - @SuppressWarnings({"WeakerAccess"}) - public void finish(String taskId) { - Log.d(TAG, "- " + ACTION_FINISH + ": " + taskId); - - BGTask task = BGTask.getTask(taskId); - if (task != null) { - task.finish(); - } - - BackgroundFetchConfig config = getConfig(taskId); - - if ((config != null) && !config.getPeriodic()) { - config.destroy(mContext); - synchronized (mConfig) { - mConfig.remove(taskId); - } - } - } - - public int status() { - return STATUS_AVAILABLE; - } - - BackgroundFetch.Callback getFetchCallback() { - return mFetchCallback; - } - - void onFetch(final BGTask task) { - BGTask.addTask(task); - Log.d(TAG, "- Background Fetch event received: " + task.getTaskId()); - synchronized (mConfig) { - if (mConfig.isEmpty()) { - BackgroundFetchConfig.load(mContext, new BackgroundFetchConfig.OnLoadCallback() { - @Override - public void onLoad(List result) { - synchronized (mConfig) { - for (BackgroundFetchConfig config : result) { - mConfig.put(config.getTaskId(), config); - } - } - doFetch(task); - } - }); - - return; - } - } - doFetch(task); - } - - private void registerTask(String taskId) { - Log.d(TAG, "- registerTask: " + taskId); - - BackgroundFetchConfig config = getConfig(taskId); - - if (config == null) { - Log.e(TAG, "- registerTask failed to find BackgroundFetchConfig for taskId " + taskId); - return; - } - config.save(mContext); - - BGTask.schedule(mContext, config); - } - - private void doFetch(BGTask task) { - BackgroundFetchConfig config = getConfig(task.getTaskId()); - - if (config == null) { - BGTask.cancel(mContext, task.getTaskId(), task.getJobId()); - return; - } - - if (isMainActivityActive()) { - if (mFetchCallback != null) { - mFetchCallback.onFetch(task.getTaskId()); - } - } else if (config.getStopOnTerminate()) { - Log.d(TAG, "- Stopping on terminate"); - stop(task.getTaskId()); - } else if (config.getJobService() != null) { - try { - task.fireHeadlessEvent(mContext, config); - } catch (BGTask.Error e) { - Log.e(TAG, "Headless task error: " + e.getMessage()); - e.printStackTrace(); - } - } else { - // {stopOnTerminate: false, forceReload: false} with no Headless JobService?? Don't know what else to do here but stop - Log.w(TAG, "- BackgroundFetch event has occurred while app is terminated but there's no jobService configured to handle the event. BackgroundFetch will terminate."); - finish(task.getTaskId()); - stop(task.getTaskId()); - } - } - - @SuppressWarnings({"WeakerAccess", "deprecation"}) - public Boolean isMainActivityActive() { - Boolean isActive = false; - - if (mContext == null || mFetchCallback == null) { - return false; - } - ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - try { - List tasks = activityManager.getRunningTasks(Integer.MAX_VALUE); - for (ActivityManager.RunningTaskInfo task : tasks) { - if (mContext.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName())) { - isActive = true; - break; - } - } - } catch (java.lang.SecurityException e) { - Log.w(TAG, "TSBackgroundFetch attempted to determine if MainActivity is active but was stopped due to a missing permission. Please add the permission 'android.permission.GET_TASKS' to your AndroidManifest. See Installation steps for more information"); - throw e; - } - return isActive; - } - - BackgroundFetchConfig getConfig(String taskId) { - synchronized (mConfig) { - return (mConfig.containsKey(taskId)) ? mConfig.get(taskId) : null; - } - } - - /** - * @interface BackgroundFetch.Callback - */ - public interface Callback { - void onFetch(String taskId); - void onTimeout(String taskId); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetchConfig.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetchConfig.java deleted file mode 100644 index 5ff655fe9..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BackgroundFetchConfig.java +++ /dev/null @@ -1,362 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.app.job.JobInfo; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Build; -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Created by chris on 2018-01-11. - */ - -public class BackgroundFetchConfig { - private Builder config; - - private static final int MINIMUM_FETCH_INTERVAL = 1; - private static final int DEFAULT_FETCH_INTERVAL = 15; - - public static final String FIELD_TASK_ID = "taskId"; - public static final String FIELD_MINIMUM_FETCH_INTERVAL = "minimumFetchInterval"; - public static final String FIELD_START_ON_BOOT = "startOnBoot"; - public static final String FIELD_REQUIRED_NETWORK_TYPE = "requiredNetworkType"; - public static final String FIELD_REQUIRES_BATTERY_NOT_LOW = "requiresBatteryNotLow"; - public static final String FIELD_REQUIRES_CHARGING = "requiresCharging"; - public static final String FIELD_REQUIRES_DEVICE_IDLE = "requiresDeviceIdle"; - public static final String FIELD_REQUIRES_STORAGE_NOT_LOW = "requiresStorageNotLow"; - public static final String FIELD_STOP_ON_TERMINATE = "stopOnTerminate"; - public static final String FIELD_JOB_SERVICE = "jobService"; - public static final String FIELD_FORCE_ALARM_MANAGER = "forceAlarmManager"; - public static final String FIELD_PERIODIC = "periodic"; - public static final String FIELD_DELAY = "delay"; - public static final String FIELD_IS_FETCH_TASK = "isFetchTask"; - - public static class Builder { - private String taskId; - private int minimumFetchInterval = DEFAULT_FETCH_INTERVAL; - private long delay = -1; - private boolean periodic = false; - private boolean forceAlarmManager = false; - private boolean stopOnTerminate = true; - private boolean startOnBoot = false; - private int requiredNetworkType = 0; - private boolean requiresBatteryNotLow = false; - private boolean requiresCharging = false; - private boolean requiresDeviceIdle = false; - private boolean requiresStorageNotLow = false; - private boolean isFetchTask = false; - - private String jobService = null; - - public Builder setTaskId(String taskId) { - this.taskId = taskId; - return this; - } - - public Builder setIsFetchTask(boolean value) { - this.isFetchTask = value; - return this; - } - - public Builder setMinimumFetchInterval(int fetchInterval) { - if (fetchInterval >= MINIMUM_FETCH_INTERVAL) { - this.minimumFetchInterval = fetchInterval; - } - return this; - } - - public Builder setStopOnTerminate(boolean stopOnTerminate) { - this.stopOnTerminate = stopOnTerminate; - return this; - } - - public Builder setStartOnBoot(boolean startOnBoot) { - this.startOnBoot = startOnBoot; - return this; - } - - public Builder setRequiredNetworkType(int networkType) { - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - if ( - (networkType != JobInfo.NETWORK_TYPE_ANY) && - (networkType != JobInfo.NETWORK_TYPE_CELLULAR) && - (networkType != JobInfo.NETWORK_TYPE_NONE) && - (networkType != JobInfo.NETWORK_TYPE_NOT_ROAMING) && - (networkType != JobInfo.NETWORK_TYPE_UNMETERED) - ) { - Log.e(BackgroundFetch.TAG, "[ERROR] Invalid " + FIELD_REQUIRED_NETWORK_TYPE + ": " + networkType + "; Defaulting to NETWORK_TYPE_NONE"); - networkType = JobInfo.NETWORK_TYPE_NONE; - } - this.requiredNetworkType = networkType; - } - return this; - } - - public Builder setRequiresBatteryNotLow(boolean value) { - this.requiresBatteryNotLow = value; - return this; - } - - public Builder setRequiresCharging(boolean value) { - this.requiresCharging = value; - return this; - } - - public Builder setRequiresDeviceIdle(boolean value) { - this.requiresDeviceIdle = value; - return this; - } - - public Builder setRequiresStorageNotLow(boolean value) { - this.requiresStorageNotLow = value; - return this; - } - - public Builder setJobService(String className) { - this.jobService = className; - return this; - } - - public Builder setForceAlarmManager(boolean value) { - this.forceAlarmManager = value; - return this; - } - - public Builder setPeriodic(boolean value) { - this.periodic = value; - return this; - } - - public Builder setDelay(long value) { - this.delay = value; - return this; - } - - public BackgroundFetchConfig build() { - return new BackgroundFetchConfig(this); - } - - public BackgroundFetchConfig load(Context context, String taskId) { - SharedPreferences preferences = context.getSharedPreferences(BackgroundFetch.TAG + ":" + taskId, 0); - if (preferences.contains(FIELD_TASK_ID)) { - setTaskId(preferences.getString(FIELD_TASK_ID, taskId)); - } - if (preferences.contains(FIELD_IS_FETCH_TASK)) { - setIsFetchTask(preferences.getBoolean(FIELD_IS_FETCH_TASK, isFetchTask)); - } - if (preferences.contains(FIELD_MINIMUM_FETCH_INTERVAL)) { - setMinimumFetchInterval(preferences.getInt(FIELD_MINIMUM_FETCH_INTERVAL, minimumFetchInterval)); - } - if (preferences.contains(FIELD_STOP_ON_TERMINATE)) { - setStopOnTerminate(preferences.getBoolean(FIELD_STOP_ON_TERMINATE, stopOnTerminate)); - } - if (preferences.contains(FIELD_REQUIRED_NETWORK_TYPE)) { - setRequiredNetworkType(preferences.getInt(FIELD_REQUIRED_NETWORK_TYPE, requiredNetworkType)); - } - if (preferences.contains(FIELD_REQUIRES_BATTERY_NOT_LOW)) { - setRequiresBatteryNotLow(preferences.getBoolean(FIELD_REQUIRES_BATTERY_NOT_LOW, requiresBatteryNotLow)); - } - if (preferences.contains(FIELD_REQUIRES_CHARGING)) { - setRequiresCharging(preferences.getBoolean(FIELD_REQUIRES_CHARGING, requiresCharging)); - } - if (preferences.contains(FIELD_REQUIRES_DEVICE_IDLE)) { - setRequiresDeviceIdle(preferences.getBoolean(FIELD_REQUIRES_DEVICE_IDLE, requiresDeviceIdle)); - } - if (preferences.contains(FIELD_REQUIRES_STORAGE_NOT_LOW)) { - setRequiresStorageNotLow(preferences.getBoolean(FIELD_REQUIRES_STORAGE_NOT_LOW, requiresStorageNotLow)); - } - if (preferences.contains(FIELD_START_ON_BOOT)) { - setStartOnBoot(preferences.getBoolean(FIELD_START_ON_BOOT, startOnBoot)); - } - if (preferences.contains(FIELD_JOB_SERVICE)) { - setJobService(preferences.getString(FIELD_JOB_SERVICE, null)); - } - if (preferences.contains(FIELD_FORCE_ALARM_MANAGER)) { - setForceAlarmManager(preferences.getBoolean(FIELD_FORCE_ALARM_MANAGER, forceAlarmManager)); - } - if (preferences.contains(FIELD_PERIODIC)) { - setPeriodic(preferences.getBoolean(FIELD_PERIODIC, periodic)); - } - if (preferences.contains(FIELD_DELAY)) { - setDelay(preferences.getLong(FIELD_DELAY, delay)); - } - return new BackgroundFetchConfig(this); - } - } - - private BackgroundFetchConfig(Builder builder) { - config = builder; - // Validate config - if (config.jobService == null) { - if (!config.stopOnTerminate) { - Log.w(BackgroundFetch.TAG, "- Configuration error: In order to use stopOnTerminate: false, you must set enableHeadless: true"); - config.setStopOnTerminate(true); - } - if (config.startOnBoot) { - Log.w(BackgroundFetch.TAG, "- Configuration error: In order to use startOnBoot: true, you must enableHeadless: true"); - config.setStartOnBoot(false); - } - } - } - - void save(Context context) { - SharedPreferences preferences = context.getSharedPreferences(BackgroundFetch.TAG, 0); - Set taskIds = preferences.getStringSet("tasks", new HashSet()); - if (taskIds == null) { - taskIds = new HashSet<>(); - } - if (!taskIds.contains(config.taskId)) { - Set newIds = new HashSet<>(taskIds); - newIds.add(config.taskId); - - SharedPreferences.Editor editor = preferences.edit(); - editor.putStringSet("tasks", newIds); - editor.apply(); - } - - SharedPreferences.Editor editor = context.getSharedPreferences(BackgroundFetch.TAG + ":" + config.taskId, 0).edit(); - - editor.putString(FIELD_TASK_ID, config.taskId); - editor.putBoolean(FIELD_IS_FETCH_TASK, config.isFetchTask); - editor.putInt(FIELD_MINIMUM_FETCH_INTERVAL, config.minimumFetchInterval); - editor.putBoolean(FIELD_STOP_ON_TERMINATE, config.stopOnTerminate); - editor.putBoolean(FIELD_START_ON_BOOT, config.startOnBoot); - editor.putInt(FIELD_REQUIRED_NETWORK_TYPE, config.requiredNetworkType); - editor.putBoolean(FIELD_REQUIRES_BATTERY_NOT_LOW, config.requiresBatteryNotLow); - editor.putBoolean(FIELD_REQUIRES_CHARGING, config.requiresCharging); - editor.putBoolean(FIELD_REQUIRES_DEVICE_IDLE, config.requiresDeviceIdle); - editor.putBoolean(FIELD_REQUIRES_STORAGE_NOT_LOW, config.requiresStorageNotLow); - editor.putString(FIELD_JOB_SERVICE, config.jobService); - editor.putBoolean(FIELD_FORCE_ALARM_MANAGER, config.forceAlarmManager); - editor.putBoolean(FIELD_PERIODIC, config.periodic); - editor.putLong(FIELD_DELAY, config.delay); - - editor.apply(); - } - - void destroy(Context context) { - SharedPreferences preferences = context.getSharedPreferences(BackgroundFetch.TAG, 0); - Set taskIds = preferences.getStringSet("tasks", new HashSet()); - if (taskIds == null) { - taskIds = new HashSet<>(); - } - if (taskIds.contains(config.taskId)) { - Set newIds = new HashSet<>(taskIds); - newIds.remove(config.taskId); - SharedPreferences.Editor editor = preferences.edit(); - editor.putStringSet("tasks", newIds); - editor.apply(); - } - if (!config.isFetchTask) { - SharedPreferences.Editor editor = context.getSharedPreferences(BackgroundFetch.TAG + ":" + config.taskId, 0).edit(); - editor.clear(); - editor.apply(); - } - } - - static int FETCH_JOB_ID = 999; - - boolean isFetchTask() { - return config.isFetchTask; - } - - public String getTaskId() { return config.taskId; } - public int getMinimumFetchInterval() { - return config.minimumFetchInterval; - } - - public int getRequiredNetworkType() { return config.requiredNetworkType; } - public boolean getRequiresBatteryNotLow() { return config.requiresBatteryNotLow; } - public boolean getRequiresCharging() { return config.requiresCharging; } - public boolean getRequiresDeviceIdle() { return config.requiresDeviceIdle; } - public boolean getRequiresStorageNotLow() { return config.requiresStorageNotLow; } - public boolean getStopOnTerminate() { - return config.stopOnTerminate; - } - public boolean getStartOnBoot() { - return config.startOnBoot; - } - - public String getJobService() { return config.jobService; } - - public boolean getForceAlarmManager() { - return config.forceAlarmManager; - } - - public boolean getPeriodic() { - return config.periodic || isFetchTask(); - } - - public long getDelay() { - return config.delay; - } - - int getJobId() { - if (config.forceAlarmManager) { - return 0; - } else { - return (isFetchTask()) ? FETCH_JOB_ID : config.taskId.hashCode(); - } - } - - public String toString() { - JSONObject output = new JSONObject(); - try { - output.put(FIELD_TASK_ID, config.taskId); - output.put(FIELD_IS_FETCH_TASK, config.isFetchTask); - output.put(FIELD_MINIMUM_FETCH_INTERVAL, config.minimumFetchInterval); - output.put(FIELD_STOP_ON_TERMINATE, config.stopOnTerminate); - output.put(FIELD_REQUIRED_NETWORK_TYPE, config.requiredNetworkType); - output.put(FIELD_REQUIRES_BATTERY_NOT_LOW, config.requiresBatteryNotLow); - output.put(FIELD_REQUIRES_CHARGING, config.requiresCharging); - output.put(FIELD_REQUIRES_DEVICE_IDLE, config.requiresDeviceIdle); - output.put(FIELD_REQUIRES_STORAGE_NOT_LOW, config.requiresStorageNotLow); - output.put(FIELD_START_ON_BOOT, config.startOnBoot); - output.put(FIELD_JOB_SERVICE, config.jobService); - output.put(FIELD_FORCE_ALARM_MANAGER, config.forceAlarmManager); - output.put(FIELD_PERIODIC, getPeriodic()); - output.put(FIELD_DELAY, config.delay); - - return output.toString(2); - } catch (JSONException e) { - e.printStackTrace(); - return output.toString(); - } - } - - static void load(final Context context, final OnLoadCallback callback) { - BackgroundFetch.getThreadPool().execute(new Runnable() { - @Override - public void run() { - final List result = new ArrayList<>(); - - SharedPreferences preferences = context.getSharedPreferences(BackgroundFetch.TAG, 0); - Set taskIds = preferences.getStringSet("tasks", new HashSet()); - - if (taskIds != null) { - for (String taskId : taskIds) { - result.add(new BackgroundFetchConfig.Builder().load(context, taskId)); - } - } - BackgroundFetch.getUiHandler().post(new Runnable() { - @Override public void run() { - callback.onLoad(result); - } - }); - } - }); - } - - interface OnLoadCallback { - void onLoad(Listconfig); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BootReceiver.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BootReceiver.java deleted file mode 100644 index b161c082e..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/BootReceiver.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -/** - * Created by chris on 2018-01-15. - */ - -public class BootReceiver extends BroadcastReceiver { - - @Override - public void onReceive(final Context context, Intent intent) { - String action = intent.getAction(); - Log.d(BackgroundFetch.TAG, "BootReceiver: " + action); - BackgroundFetch.getThreadPool().execute(new Runnable() { - @Override public void run() { - BackgroundFetch.getInstance(context.getApplicationContext()).onBoot(); - } - }); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchAlarmReceiver.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchAlarmReceiver.java deleted file mode 100644 index afcb900dd..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchAlarmReceiver.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.PowerManager; -import android.util.Log; - -import static android.content.Context.POWER_SERVICE; - -/** - * Created by chris on 2018-01-11. - */ - -public class FetchAlarmReceiver extends BroadcastReceiver { - - @Override - public void onReceive(final Context context, Intent intent) { - PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE); - final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, BackgroundFetch.TAG + "::" + intent.getAction()); - // WakeLock expires in MAX_TIME + 4s buffer. - wakeLock.acquire((BGTask.MAX_TIME + 4000)); - - final String taskId = intent.getAction(); - - final FetchJobService.CompletionHandler completionHandler = new FetchJobService.CompletionHandler() { - @Override - public void finish() { - if (wakeLock.isHeld()) { - wakeLock.release(); - Log.d(BackgroundFetch.TAG, "- FetchAlarmReceiver finish"); - } - } - }; - - BGTask task = new BGTask(context, taskId, completionHandler, 0); - - BackgroundFetch.getInstance(context.getApplicationContext()).onFetch(task); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchJobService.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchJobService.java deleted file mode 100644 index 0938d5356..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/java/com/transistorsoft/tsbackgroundfetch/FetchJobService.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import android.annotation.TargetApi; -import android.app.job.JobParameters; -import android.app.job.JobService; -import android.os.PersistableBundle; -import android.util.Log; - -/** - * Created by chris on 2018-01-11. - */ -@TargetApi(21) -public class FetchJobService extends JobService { - @Override - public boolean onStartJob(final JobParameters params) { - PersistableBundle extras = params.getExtras(); - final String taskId = extras.getString(BackgroundFetchConfig.FIELD_TASK_ID); - - CompletionHandler completionHandler = new CompletionHandler() { - @Override - public void finish() { - Log.d(BackgroundFetch.TAG, "- jobFinished"); - jobFinished(params, false); - } - }; - BGTask task = new BGTask(this, taskId, completionHandler, params.getJobId()); - BackgroundFetch.getInstance(getApplicationContext()).onFetch(task); - - return true; - } - - @Override - public boolean onStopJob(final JobParameters params) { - Log.d(BackgroundFetch.TAG, "- onStopJob"); - - PersistableBundle extras = params.getExtras(); - final String taskId = extras.getString(BackgroundFetchConfig.FIELD_TASK_ID); - - BGTask task = BGTask.getTask(taskId); - if (task != null) { - task.onTimeout(getApplicationContext()); - } - jobFinished(params, false); - return true; - } - - public interface CompletionHandler { - void finish(); - } -} diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/res/values/strings.xml b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/res/values/strings.xml deleted file mode 100644 index 17b4bbaa0..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - TSBackgroundFetch - diff --git a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/test/java/com/transistorsoft/tsbackgroundfetch/ExampleUnitTest.java b/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/test/java/com/transistorsoft/tsbackgroundfetch/ExampleUnitTest.java deleted file mode 100644 index 99449e071..000000000 --- a/mobile/thirdparty/transistor-background-fetch/android/tsbackgroundfetch/src/test/java/com/transistorsoft/tsbackgroundfetch/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.transistorsoft.tsbackgroundfetch; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/.gitattributes b/server/.gitattributes similarity index 100% rename from .gitattributes rename to server/.gitattributes diff --git a/server/README.md b/server/README.md index 9c4b2f696..06fdcb518 100644 --- a/server/README.md +++ b/server/README.md @@ -113,6 +113,11 @@ repository's [Discussions](https://github.com/ente-io/ente/discussions), or on try to clarify, and also document such FAQs. Please feel free to open documentation PRs around this too. +> [!TIP] +> +> You can find more guides and documentation around self-hosting at +> [help.ente.io/self-hosting](https://help.ente.io/self-hosting). + ## Thanks ❤️ We've had great fun with this combination (Golang + Postgres + Docker), and we diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index 9403efaa5..14ceb3e3b 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -620,6 +620,7 @@ func main() { adminAPI.POST("/user/disable-2fa", adminHandler.DisableTwoFactor) adminAPI.POST("/user/disable-passkeys", adminHandler.RemovePasskeys) adminAPI.POST("/user/close-family", adminHandler.CloseFamily) + adminAPI.PUT("/user/change-email", adminHandler.ChangeEmail) adminAPI.DELETE("/user/delete", adminHandler.DeleteUser) adminAPI.POST("/user/recover", adminHandler.RecoverAccount) adminAPI.GET("/email-hash", adminHandler.GetEmailHash) diff --git a/server/ente/admin.go b/server/ente/admin.go index 66ad27741..c56e48e5b 100644 --- a/server/ente/admin.go +++ b/server/ente/admin.go @@ -46,6 +46,11 @@ type UpdateSubscriptionRequest struct { Attributes SubscriptionAttributes `json:"attributes"` } +type ChangeEmailRequest struct { + UserID int64 `json:"userID" binding:"required"` + Email string `json:"email" binding:"required"` +} + type AddOnAction string const ( diff --git a/server/pkg/api/admin.go b/server/pkg/api/admin.go index 90c9fd3e6..b153e19bb 100644 --- a/server/pkg/api/admin.go +++ b/server/pkg/api/admin.go @@ -306,6 +306,25 @@ func (h *AdminHandler) UpdateSubscription(c *gin.Context) { c.JSON(http.StatusOK, gin.H{}) } +func (h *AdminHandler) ChangeEmail(c *gin.Context) { + var r ente.ChangeEmailRequest + if err := c.ShouldBindJSON(&r); err != nil { + handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "Bad request")) + return + } + adminID := auth.GetUserID(c.Request.Header) + go h.DiscordController.NotifyAdminAction( + fmt.Sprintf("Admin (%d) updating email for user: %d", adminID, r.UserID)) + err := h.UserController.UpdateEmail(c, r.UserID, r.Email) + if err != nil { + logrus.WithError(err).Error("Failed to update email") + handler.Error(c, stacktrace.Propagate(err, "")) + return + } + logrus.Info("Updated email") + c.JSON(http.StatusOK, gin.H{}) +} + func (h *AdminHandler) ReQueueItem(c *gin.Context) { var r ente.ReQueueItemRequest if err := c.ShouldBindJSON(&r); err != nil { diff --git a/server/pkg/controller/user/userauth.go b/server/pkg/controller/user/userauth.go index bf092cb14..bbc9942de 100644 --- a/server/pkg/controller/user/userauth.go +++ b/server/pkg/controller/user/userauth.go @@ -197,7 +197,13 @@ func (c *UserController) ChangeEmail(ctx *gin.Context, request ente.EmailVerific if err != nil { return stacktrace.Propagate(err, "") } - _, err = c.UserRepo.GetUserIDWithEmail(email) + + return c.UpdateEmail(ctx, auth.GetUserID(ctx.Request.Header), email) +} + +// UpdateEmail updates the email address of the user with the provided userID +func (c *UserController) UpdateEmail(ctx *gin.Context, userID int64, email string) error { + _, err := c.UserRepo.GetUserIDWithEmail(email) if err == nil { // email already owned by a user return stacktrace.Propagate(ente.ErrPermissionDenied, "") @@ -206,7 +212,6 @@ func (c *UserController) ChangeEmail(ctx *gin.Context, request ente.EmailVerific // unknown error, rethrow return stacktrace.Propagate(err, "") } - userID := auth.GetUserID(ctx.Request.Header) user, err := c.UserRepo.Get(userID) if err != nil { return stacktrace.Propagate(err, "") diff --git a/server/scripts/images/museum.png b/server/scripts/images/museum.png index a93370da8..bd837dc36 100644 Binary files a/server/scripts/images/museum.png and b/server/scripts/images/museum.png differ diff --git a/web/apps/photos/.env.development b/web/apps/photos/.env.development index 038f0237a..548e5bbfb 100644 --- a/web/apps/photos/.env.development +++ b/web/apps/photos/.env.development @@ -68,7 +68,12 @@ # # NEXT_PUBLIC_ENTE_FAMILY_PORTAL_ENDPOINT = http://localhost:3003 -# The path of the JSON file which contains the expected results of our -# integration tests. See `upload.test.ts` for more details. +# The JSON which describes the expected results of our integration tests. See +# `upload.test.ts` for more details of the expected format. # -# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH = /path/to/dataset/expected.json +# This is perhaps easier to specify as an environment variable, since then we +# can directly read from the source file when running `yarn dev`. For example, +# +# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON=`cat path/to/expected.json` yarn dev +# +# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON = {} diff --git a/web/apps/photos/public/locales/de-DE/translation.json b/web/apps/photos/public/locales/de-DE/translation.json index f5a6f13b3..a515e7366 100644 --- a/web/apps/photos/public/locales/de-DE/translation.json +++ b/web/apps/photos/public/locales/de-DE/translation.json @@ -277,7 +277,7 @@ "START": "Start", "LAST_EXPORT_TIME": "Letztes Exportdatum", "EXPORT_AGAIN": "Neusynchronisation", - "LOCAL_STORAGE_NOT_ACCESSIBLE": "", + "LOCAL_STORAGE_NOT_ACCESSIBLE": "Lokaler Speicher nicht zugänglich", "LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE": "", "SEND_OTT": "OTP senden", "EMAIl_ALREADY_OWNED": "Diese E-Mail wird bereits verwendet", @@ -356,22 +356,22 @@ "DATE_TIME_ORIGINAL": "", "DATE_TIME_DIGITIZED": "", "METADATA_DATE": "", - "CUSTOM_TIME": "", + "CUSTOM_TIME": "Benutzerdefinierte Zeit", "REOPEN_PLAN_SELECTOR_MODAL": "", "OPEN_PLAN_SELECTOR_MODAL_FAILED": "", "INSTALL": "Installieren", "SHARING_DETAILS": "Details teilen", - "MODIFY_SHARING": "", - "ADD_COLLABORATORS": "", - "ADD_NEW_EMAIL": "", + "MODIFY_SHARING": "Freigabe ändern", + "ADD_COLLABORATORS": "Bearbeiter hinzufügen", + "ADD_NEW_EMAIL": "Neue E-Mail-Adresse hinzufügen", "shared_with_people_zero": "", - "shared_with_people_one": "", + "shared_with_people_one": "Geteilt mit einer Person", "shared_with_people_other": "", - "participants_zero": "", - "participants_one": "", + "participants_zero": "Keine Teilnehmer", + "participants_one": "1 Teilnehmer", "participants_other": "", - "ADD_VIEWERS": "", - "PARTICIPANTS": "", + "ADD_VIEWERS": "Betrachter hinzufügen", + "PARTICIPANTS": "Teilnehmer", "CHANGE_PERMISSIONS_TO_VIEWER": "", "CHANGE_PERMISSIONS_TO_COLLABORATOR": "", "CONVERT_TO_VIEWER": "Ja, zu \"Beobachter\" ändern", diff --git a/web/apps/photos/public/locales/ko-KR/translation.json b/web/apps/photos/public/locales/ko-KR/translation.json index 754fef892..7482dd397 100644 --- a/web/apps/photos/public/locales/ko-KR/translation.json +++ b/web/apps/photos/public/locales/ko-KR/translation.json @@ -2,80 +2,80 @@ "HERO_SLIDE_1_TITLE": "추억을 안전하게 백업하세요", "HERO_SLIDE_1": "종단간 암호화가 기본지원입니다", "HERO_SLIDE_2_TITLE": "낙진대피소에 안전하게 보관됩니다", - "HERO_SLIDE_2": "", - "HERO_SLIDE_3_TITLE": "", - "HERO_SLIDE_3": "", - "LOGIN": "", - "SIGN_UP": "", - "NEW_USER": "", - "EXISTING_USER": "", - "ENTER_NAME": "", - "PUBLIC_UPLOADER_NAME_MESSAGE": "", - "ENTER_EMAIL": "", - "EMAIL_ERROR": "", - "REQUIRED": "", - "EMAIL_SENT": "", - "CHECK_INBOX": "", - "ENTER_OTT": "", - "RESEND_MAIL": "", - "VERIFY": "", - "UNKNOWN_ERROR": "", - "INVALID_CODE": "", - "EXPIRED_CODE": "", - "SENDING": "", - "SENT": "", - "PASSWORD": "", - "LINK_PASSWORD": "", - "RETURN_PASSPHRASE_HINT": "", - "SET_PASSPHRASE": "", - "VERIFY_PASSPHRASE": "", - "INCORRECT_PASSPHRASE": "", - "ENTER_ENC_PASSPHRASE": "", - "PASSPHRASE_DISCLAIMER": "", - "WELCOME_TO_ENTE_HEADING": "", - "WELCOME_TO_ENTE_SUBHEADING": "", - "WHERE_YOUR_BEST_PHOTOS_LIVE": "", - "KEY_GENERATION_IN_PROGRESS_MESSAGE": "", - "PASSPHRASE_HINT": "", - "CONFIRM_PASSPHRASE": "", - "REFERRAL_CODE_HINT": "", - "REFERRAL_INFO": "", - "PASSPHRASE_MATCH_ERROR": "", - "CREATE_COLLECTION": "", - "ENTER_ALBUM_NAME": "", - "CLOSE_OPTION": "", - "ENTER_FILE_NAME": "", - "CLOSE": "", - "NO": "", - "NOTHING_HERE": "", - "UPLOAD": "", - "IMPORT": "", - "ADD_PHOTOS": "", - "ADD_MORE_PHOTOS": "", - "add_photos_one": "", - "add_photos_other": "", - "SELECT_PHOTOS": "", - "FILE_UPLOAD": "", + "HERO_SLIDE_2": "오랫동안 보존할 수 있도록한 설계", + "HERO_SLIDE_3_TITLE": "
어디에서나
이용가능
", + "HERO_SLIDE_3": "안드로이드, iOS, 웹, 데스크탑", + "LOGIN": "로그인", + "SIGN_UP": "회원가입", + "NEW_USER": "ente의 새소식", + "EXISTING_USER": "기존 사용자", + "ENTER_NAME": "이름 입력", + "PUBLIC_UPLOADER_NAME_MESSAGE": "친구들이 이 멋진 사진에 대해 고마워할 수 있도록 이름을 추가하세요!", + "ENTER_EMAIL": "이메일 주소를 입력하세요", + "EMAIL_ERROR": "올바른 이메일을 입력하세요", + "REQUIRED": "필수", + "EMAIL_SENT": "{{email}} 로 인증 코드가 전송되었습니다", + "CHECK_INBOX": "인증을 완료하기 위해 당신의 메일 수신함(그리고 스팸 수신함)을 확인하세요.", + "ENTER_OTT": "인증 코드", + "RESEND_MAIL": "코드 재전송하기", + "VERIFY": "인증", + "UNKNOWN_ERROR": "문제가 생긴 것 같아요. 다시 시도하세요", + "INVALID_CODE": "잘못된 인증 코드", + "EXPIRED_CODE": "입력한 인증 코드가 만료되었습니다", + "SENDING": "전송 중...", + "SENT": "발송 완료!", + "PASSWORD": "비밀번호", + "LINK_PASSWORD": "앨범 잠금해제를 위해 비밀번호를 입력하세요", + "RETURN_PASSPHRASE_HINT": "비밀번호", + "SET_PASSPHRASE": "비밀번호 설정", + "VERIFY_PASSPHRASE": "로그인", + "INCORRECT_PASSPHRASE": "잘못된 비밀번호입니다", + "ENTER_ENC_PASSPHRASE": "당신의 데이터를 암호화하는 데 사용할 수 있는 비밀번호를 입력하세요", + "PASSPHRASE_DISCLAIMER": "우리는 귀하의 비밀번호를 저장하지 않습니다. 만약 비밀번호를 잊어버린 경우 복구 키 없다면 데이터 복구를 도와드릴 수 없습니다.", + "WELCOME_TO_ENTE_HEADING": "환영합니다 ", + "WELCOME_TO_ENTE_SUBHEADING": "End-to-End 암호화된 사진 저장 및 공유", + "WHERE_YOUR_BEST_PHOTOS_LIVE": "당신 최고의 사진이 있는 곳", + "KEY_GENERATION_IN_PROGRESS_MESSAGE": "암호 키 생성 중...", + "PASSPHRASE_HINT": "비밀번호", + "CONFIRM_PASSPHRASE": "비밀번호 확인", + "REFERRAL_CODE_HINT": "어떻게 Ente에 대해 들으셨나요? (선택사항)", + "REFERRAL_INFO": "우리는 앱 설치를 추적하지 않습니다. 우리를 알게 된 곳을 남겨주시면 우리에게 도움이 될꺼에요!", + "PASSPHRASE_MATCH_ERROR": "비밀번호가 일치하지 않습니다", + "CREATE_COLLECTION": "새 앨범", + "ENTER_ALBUM_NAME": "앨범 이름", + "CLOSE_OPTION": "닫기 (Esc)", + "ENTER_FILE_NAME": "파일 이름", + "CLOSE": "닫기", + "NO": "아니오", + "NOTHING_HERE": "아직 볼 수 있는 것이 없어요 👀", + "UPLOAD": "업로드", + "IMPORT": "가져오기", + "ADD_PHOTOS": "사진 추가", + "ADD_MORE_PHOTOS": "사진 더 추가하기", + "add_photos_one": "아이템 하나 추가", + "add_photos_other": "아이템 {{count, number}} 개 추가하기", + "SELECT_PHOTOS": "사진 선택하기", + "FILE_UPLOAD": "파일 업로드", "UPLOAD_STAGE_MESSAGE": { - "0": "", - "1": "", - "2": "", - "3": "", - "4": "", - "5": "" + "0": "업로드 준비중", + "1": "구글 메타데이타 파일들 읽는중", + "2": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} 파일 메타데이터가 추출되었습니다", + "3": "{{uploadCounter.finished, number}} / {{uploadCounter.total, number}} 파일이 처리되었습니다", + "4": "남은 업로드 취소중", + "5": "백업 완료" }, - "FILE_NOT_UPLOADED_LIST": "", - "SUBSCRIPTION_EXPIRED": "", - "SUBSCRIPTION_EXPIRED_MESSAGE": "", - "STORAGE_QUOTA_EXCEEDED": "", - "INITIAL_LOAD_DELAY_WARNING": "", - "USER_DOES_NOT_EXIST": "", - "NO_ACCOUNT": "", - "ACCOUNT_EXISTS": "", - "CREATE": "", - "DOWNLOAD": "", - "DOWNLOAD_OPTION": "", - "DOWNLOAD_FAVORITES": "", + "FILE_NOT_UPLOADED_LIST": "아래 파일들은 업로드 되지 않았습니다", + "SUBSCRIPTION_EXPIRED": "구독 만료", + "SUBSCRIPTION_EXPIRED_MESSAGE": "당신 구독이 만료되었으니, 구독을 갱신해주세요", + "STORAGE_QUOTA_EXCEEDED": "스토리지 제한이 초과되었습니다", + "INITIAL_LOAD_DELAY_WARNING": "처음 로딩시 다소 시간이 걸릴 수 있습니다", + "USER_DOES_NOT_EXIST": "죄송합니다. 해당 이메일을 사용하는 사용자를 찾을 수 없습니다", + "NO_ACCOUNT": "계정이 없습니다", + "ACCOUNT_EXISTS": "이미 계정이 있습니다", + "CREATE": "만들기", + "DOWNLOAD": "다운로드", + "DOWNLOAD_OPTION": "다운로드 (D)", + "DOWNLOAD_FAVORITES": "즐겨찾기 다운로드", "DOWNLOAD_UNCATEGORIZED": "", "DOWNLOAD_HIDDEN_ITEMS": "", "COPY_OPTION": "", diff --git a/web/apps/photos/src/components/PhotoList/index.tsx b/web/apps/photos/src/components/PhotoList/index.tsx index 3eee1d683..e6d955cda 100644 --- a/web/apps/photos/src/components/PhotoList/index.tsx +++ b/web/apps/photos/src/components/PhotoList/index.tsx @@ -780,23 +780,28 @@ export function PhotoList({ listItem: TimeStampListItem, isScrolling: boolean, ) => { + // Enhancement: This logic doesn't work on the shared album screen, the + // galleryContext.selectedFile is always null there. + const haveSelection = (galleryContext.selectedFile?.count ?? 0) > 0; switch (listItem.itemType) { case ITEM_TYPE.TIME: return listItem.dates ? ( listItem.dates .map((item) => [ - - onChangeSelectAllCheckBox(item.date) - } - size="small" - sx={{ pl: 0 }} - disableRipple={true} - /> + {haveSelection && ( + + onChangeSelectAllCheckBox(item.date) + } + size="small" + sx={{ pl: 0 }} + disableRipple={true} + /> + )} {item.date} ,
, @@ -804,17 +809,19 @@ export function PhotoList({ .flat() ) : ( - - onChangeSelectAllCheckBox(listItem.date) - } - size="small" - sx={{ pl: 0 }} - disableRipple={true} - /> + {haveSelection && ( + + onChangeSelectAllCheckBox(listItem.date) + } + size="small" + sx={{ pl: 0 }} + disableRipple={true} + /> + )} {listItem.date} ); diff --git a/web/apps/photos/src/components/PhotoViewer/index.tsx b/web/apps/photos/src/components/PhotoViewer/index.tsx index 702542172..425290023 100644 --- a/web/apps/photos/src/components/PhotoViewer/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/index.tsx @@ -178,6 +178,14 @@ function PhotoViewer(props: Iprops) { switch (event.key) { case "i": case "I": + // Enhancement: This should be calling handleOpenInfo, but + // that handling the case where a keybinding triggers an + // exit from fullscreen and opening the info drawer is not + // currently working (the info drawer opens, but the exit + // from fullscreen doesn't happen). + // + // So for now, let the keybinding only work when not in + // fullscreen instead of doing a mish-mash. setShowInfo(true); break; case "Backspace": @@ -616,7 +624,18 @@ function PhotoViewer(props: Iprops) { const handleCloseInfo = () => { setShowInfo(false); }; - const handleOpenInfo = () => { + + const handleOpenInfo = (photoSwipe: any) => { + // Get out of full screen mode if needed first to be able to show info + if (isInFullScreenMode) { + const fullScreenApi: PhotoswipeFullscreenAPI = + photoSwipe?.ui?.getFullscreenAPI(); + if (fullScreenApi && fullScreenApi.isFullscreen()) { + fullScreenApi.exit(); + setIsInFullScreenMode(false); + } + } + setShowInfo(true); }; @@ -851,7 +870,7 @@ function PhotoViewer(props: Iprops) { diff --git a/web/apps/photos/tests/upload.test.ts b/web/apps/photos/tests/upload.test.ts index dcd16db3c..6e58cf0c2 100644 --- a/web/apps/photos/tests/upload.test.ts +++ b/web/apps/photos/tests/upload.test.ts @@ -100,13 +100,13 @@ const FILE_NAME_TO_JSON_NAME = [ ]; export async function testUpload() { - const jsonPath = process.env.NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH; - if (!jsonPath) { + const jsonString = process.env.NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON; + if (!jsonString) { throw Error( - "Please specify the NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH to run the upload tests", + "Please specify the NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON to run the upload tests", ); } - const expectedState = await import(jsonPath); + const expectedState = JSON.parse(jsonString); if (!expectedState) { throw Error("upload test failed expectedState missing"); }