Merge remote-tracking branch 'origin/mobile_face' into mobile_face

This commit is contained in:
laurenspriem 2024-03-15 14:58:21 +05:30
commit ca16c6f0d6
132 changed files with 1014 additions and 2551 deletions

View file

@ -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:

View file

@ -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:

View file

@ -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"

View file

@ -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

View file

@ -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:

View file

@ -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

7
.gitmodules vendored
View file

@ -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

3
auth/.gitignore vendored
View file

@ -9,6 +9,9 @@
.history
.svn/
# Editors
.vscode/
# IntelliJ related
*.iml
*.ipr

View file

@ -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

View file

@ -0,0 +1 @@
{}

View file

@ -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"
}

View file

@ -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} に接続しました"
}

View file

@ -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}"
}

View file

@ -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."
}

View file

@ -407,6 +407,12 @@
"hearUsWhereTitle": "您是如何知道Ente的 (可选的)",
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"waitingForBrowserRequest": "正在等待浏览器请求...",
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL",
"passkey": "通行密钥"
"passkey": "通行密钥",
"developerSettingsWarning": "您确定要修改开发者设置吗?",
"developerSettings": "开发者设置",
"serverEndpoint": "服务器端点",
"invalidEndpoint": "端点无效",
"invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。",
"endpointUpdatedMessage": "端点更新成功",
"customEndpoint": "已连接至 {endpoint}"
}

View file

@ -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.

View file

@ -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:
<img width="454" alt="ente Authenticator Screenshot" src="https://github.com/gweeeen/auth/assets/41323182/30566a69-cfa0-4de0-9f0d-95967d4c5cad">
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/)

View file

@ -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 <export_file> <output_file>
```
Moved to
[help.ente.io/auth/migration-guides/export](https://help.ente.io/auth/migration-guides/export/)

1
cli/.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
docs/generated/*.md linguist-generated=true

View file

@ -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

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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" {

View file

@ -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"`
}

View file

@ -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)
}

View file

@ -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",
},
],
},
{

View file

@ -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.

View file

@ -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).

View file

@ -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.<br><br>
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:
```
./<binary-name> <path_to_export_file>
```
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.<br><br>
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).<br><br>
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.

View file

@ -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 <export_file> <output_file>
```

View file

@ -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)

View file

@ -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.

View file

@ -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`.

View file

@ -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.

View file

@ -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.

View file

@ -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
....
```

View file

@ -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).

View file

@ -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
```
```

5
mobile/.gitignore vendored
View file

@ -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

View file

@ -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`

32
mobile/docs/release.md Normal file
View file

@ -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.

View file

@ -1,3 +0,0 @@
{
"dart.flutterSdkPath": "thirdparty/flutter/bin"
}

View file

@ -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).

View file

@ -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小时内收到来自我们团队的回复。

View file

@ -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).

View file

@ -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).

View file

@ -25,17 +25,6 @@ bool sqlIntToBool(int? value, {bool defaultValue = false}) {
}
}
Map<String, dynamic> 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<String, dynamic> mapPersonToRow(Person p) {
return {
idColumn: p.remoteID,

View file

@ -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<String, dynamic> toJson() => {
'face': face.toJson(),
'personID': personID,
'confirmed': confirmed ?? false,
'close_dist': closeDist,
'close_face_id': closeFaceID,
};
// fromJson
factory PersonFace.fromJson(Map<String, dynamic> json) {
return PersonFace(
Face.fromJson(json['face'] as Map<String, dynamic>),
json['personID'] as int?,
json['close_dist'] as double?,
json['close_face_id'] as String?,
confirmed: json['confirmed'] as bool?,
);
}
}

View file

@ -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<List<Face>> downloadZip() async {
final List<Face> 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<dynamic>;
for (final item in res) {
try {
result.add(Face.fromJson(item));
} catch (e) {
_logger.warning("failed to parse $item");
rethrow;
}
}
}
final Set<String> faceID = {};
for (final face in result) {
if (faceID.contains(face.faceID)) {
_logger.warning("duplicate faceID ${face.faceID}");
}
faceID.add(face.faceID);
}
return result;
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -68,6 +68,7 @@ class GalleryFileWidget extends StatelessWidget {
: _onLongPressNoSelectionLimit(context, file);
},
child: Stack(
clipBehavior: Clip.none,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(1),

View file

@ -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:

View file

@ -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/

View file

@ -1,19 +0,0 @@
Copyright (c) 2017 Transistor Software <info@transistorsoft.com>
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.

View file

@ -1,22 +0,0 @@
Transistor Background Fetch
===========================================================================
Copyright (c) 2017 Transistor Software <info@transistorsoft.com>
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.

View file

@ -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

View file

@ -1,9 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

View file

@ -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'
}

View file

@ -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

View file

@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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());
}
}

View file

@ -1,11 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.transistorsoft.backgroundfetch">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" />
</manifest>

View file

@ -1,34 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View file

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View file

@ -1,3 +0,0 @@
<resources>
<string name="app_name">BackgroundFetch</string>
</resources>

View file

@ -1,11 +0,0 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View file

@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View file

@ -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"
}

Some files were not shown because too many files have changed in this diff Show more