mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 08:13:20 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10a718b0c7 | ||
|
|
9ec4050e4d | ||
|
|
93e2c0df7c | ||
|
|
8d07397a59 | ||
|
|
15d44b873b | ||
|
|
6a979d0ff5 | ||
|
|
a4db088eda | ||
|
|
96454b7cbf | ||
|
|
9c1df2179c | ||
|
|
52c9125404 | ||
|
|
172a857604 | ||
|
|
d8e68a266c | ||
|
|
1f57ba6c50 | ||
|
|
ff19578807 | ||
|
|
9298d57f22 | ||
|
|
8cf5d5728e | ||
|
|
bdf6d764ca | ||
|
|
05e8da4bcc | ||
|
|
382e547f74 | ||
|
|
4f9985d2b0 | ||
|
|
ef97417cd7 | ||
|
|
7bdf4d8b18 | ||
|
|
a6c95d06b5 | ||
|
|
bd4a275558 | ||
|
|
a2b46ee7cb | ||
|
|
2003ac9d2c | ||
|
|
2a5667251e | ||
|
|
79589b07fc | ||
|
|
0aed13a2cf | ||
|
|
df412e75d1 | ||
|
|
2b8dbde923 | ||
|
|
33791a03ac | ||
|
|
80a33e98a2 | ||
|
|
afed18908b | ||
|
|
fe58dea3e0 | ||
|
|
569045fcd5 | ||
|
|
fdda670311 | ||
|
|
fbb7b05b9c | ||
|
|
976eeab6d7 | ||
|
|
e61bcd2785 | ||
|
|
570edb4319 | ||
|
|
8fe8c42765 | ||
|
|
0eebe6b156 | ||
|
|
946831b37e | ||
|
|
1d4e742d66 | ||
|
|
29979f6b04 | ||
|
|
2f6e1ff477 | ||
|
|
c1030c48fa | ||
|
|
ef5d08cb75 | ||
|
|
faa6904ce3 | ||
|
|
c27da8e7c4 | ||
|
|
3ef5ca9cc0 | ||
|
|
2fbd3b4538 | ||
|
|
b3b21ea6b1 | ||
|
|
a3b4ede8f3 | ||
|
|
10ea6a86e3 | ||
|
|
3b2b37b3b0 | ||
|
|
75e27ffbe3 | ||
|
|
a2cff6da28 | ||
|
|
fe80fd0ba1 | ||
|
|
5c6b9fa471 | ||
|
|
d926565358 | ||
|
|
ce0b8bc62d | ||
|
|
04aeddc5de | ||
|
|
13ffbe911a | ||
|
|
ab04759b0e | ||
|
|
798cfef391 | ||
|
|
3b5cdfe03c | ||
|
|
63449a3832 |
14
.github/scripts/android/clean-fdroid.ps1
vendored
14
.github/scripts/android/clean-fdroid.ps1
vendored
@@ -30,16 +30,6 @@ $xml.Load($androidManifest);
|
|||||||
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||||
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
||||||
|
|
||||||
$firebaseReceiver1=$xml.SelectSingleNode(`
|
|
||||||
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdInternalReceiver']", `
|
|
||||||
$nsAndroid);
|
|
||||||
$firebaseReceiver1.ParentNode.RemoveChild($firebaseReceiver1);
|
|
||||||
|
|
||||||
$firebaseReceiver2=$xml.SelectSingleNode(`
|
|
||||||
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdReceiver']", `
|
|
||||||
$nsAndroid);
|
|
||||||
$firebaseReceiver2.ParentNode.RemoveChild($firebaseReceiver2);
|
|
||||||
|
|
||||||
$xml.Save($androidManifest);
|
$xml.Save($androidManifest);
|
||||||
|
|
||||||
Write-Output "########################################"
|
Write-Output "########################################"
|
||||||
@@ -56,6 +46,10 @@ $firebaseNode=$xml.SelectSingleNode(`
|
|||||||
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
||||||
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
||||||
|
|
||||||
|
$daggerNode=$xml.SelectSingleNode(`
|
||||||
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
|
||||||
|
$daggerNode.ParentNode.RemoveChild($daggerNode);
|
||||||
|
|
||||||
$safetyNetNode=$xml.SelectSingleNode(`
|
$safetyNetNode=$xml.SelectSingleNode(`
|
||||||
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
||||||
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
||||||
|
|||||||
34
.github/workflows/build.yml
vendored
34
.github/workflows/build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
- name: Set up cloc
|
- name: Set up cloc
|
||||||
run: |
|
run: |
|
||||||
@@ -31,22 +31,21 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@v1
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help
|
nuget help | grep Version
|
||||||
msbuild -version
|
msbuild -version
|
||||||
dotnet --info
|
dotnet --info
|
||||||
Write-Output "GitHub ref: $env:GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
Write-Output "GitHub event: $env:GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
shell: pwsh
|
|
||||||
env:
|
env:
|
||||||
GITHUB_REF: ${{ github.ref }}
|
GITHUB_REF: ${{ github.ref }}
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
GITHUB_EVENT: ${{ github.event_name }}
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||||
@@ -83,14 +82,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Play Store .aab artifact
|
- name: Upload Play Store .aab artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.aab
|
name: com.x8bit.bitwarden.aab
|
||||||
path: ./com.x8bit.bitwarden.aab
|
path: ./com.x8bit.bitwarden.aab
|
||||||
|
|
||||||
- name: Upload Play Store .apk artifact
|
- name: Upload Play Store .apk artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.apk
|
name: com.x8bit.bitwarden.apk
|
||||||
path: ./com.x8bit.bitwarden.apk
|
path: ./com.x8bit.bitwarden.apk
|
||||||
@@ -115,7 +114,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload F-Droid .apk artifact
|
- name: Upload F-Droid .apk artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
path: ./com.x8bit.bitwarden-fdroid.apk
|
path: ./com.x8bit.bitwarden-fdroid.apk
|
||||||
@@ -146,7 +145,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: '10.x'
|
||||||
|
|
||||||
@@ -181,7 +180,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
@@ -215,18 +214,17 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help
|
nuget help | grep Version
|
||||||
msbuild -version
|
msbuild -version
|
||||||
dotnet --info
|
dotnet --info
|
||||||
Write-Output "GitHub ref: $env:GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
Write-Output "GitHub event: $env:GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
shell: pwsh
|
|
||||||
env:
|
env:
|
||||||
GITHUB_REF: ${{ github.ref }}
|
GITHUB_REF: ${{ github.ref }}
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
GITHUB_EVENT: ${{ github.event_name }}
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
run: ./.github/scripts/ios/decrypt-secrets.ps1
|
run: ./.github/scripts/ios/decrypt-secrets.ps1
|
||||||
@@ -271,7 +269,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload App Store .ipa artifact
|
- name: Upload App Store .ipa artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
||||||
with:
|
with:
|
||||||
name: Bitwarden.ipa
|
name: Bitwarden.ipa
|
||||||
path: ./bitwarden-export/Bitwarden.ipa
|
path: ./bitwarden-export/Bitwarden.ipa
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://ci.appveyor.com/project/bitwarden/mobile)
|
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
|
||||||
[](https://crowdin.com/project/bitwarden-mobile)
|
[](https://crowdin.com/project/bitwarden-mobile)
|
||||||
[](https://gitter.im/bitwarden/Lobby)
|
[](https://gitter.im/bitwarden/Lobby)
|
||||||
|
|
||||||
|
|||||||
146
appveyor.yml
146
appveyor.yml
@@ -1,146 +0,0 @@
|
|||||||
image:
|
|
||||||
- Visual Studio 2019
|
|
||||||
- Ubuntu1804
|
|
||||||
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- l10n_master
|
|
||||||
- gh-pages
|
|
||||||
|
|
||||||
configuration: Release
|
|
||||||
|
|
||||||
stack: node 10
|
|
||||||
|
|
||||||
init:
|
|
||||||
- sh: |
|
|
||||||
if [ "${DEBUG_SSH}" == "true" ]
|
|
||||||
then
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:DEBUG_RDP -eq "true") {
|
|
||||||
iex ((new-object net.webclient).DownloadString(`
|
|
||||||
'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
|
||||||
}
|
|
||||||
- ps: |
|
|
||||||
if($env:APPVEYOR_REPO_TAG -eq "true") {
|
|
||||||
$tagName = $env:APPVEYOR_REPO_TAG_NAME.TrimStart("v")
|
|
||||||
$env:RELEASE_NAME = "Version ${tagName}"
|
|
||||||
}
|
|
||||||
|
|
||||||
install:
|
|
||||||
- sh: |
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/secure-file/master/install.sh' | bash -e -
|
|
||||||
./appveyor-tools/secure-file -decrypt ./store/fdroid/keystore.jks.enc -secret $FDROID_KEYSTORE_ENC_PASSWORD
|
|
||||||
- sh: npm install
|
|
||||||
- sh: |
|
|
||||||
sudo apt-get -qq update
|
|
||||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" -a "${GH_TOKEN}" != "" ]
|
|
||||||
then
|
|
||||||
git config --global credential.helper store
|
|
||||||
echo "https://${GH_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
|
|
||||||
git config --global user.email "ci@bitwarden.com"
|
|
||||||
git config --global user.name "Bitwarden CI"
|
|
||||||
fi
|
|
||||||
- cmd: choco install cloc --no-progress
|
|
||||||
- cmd: "cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML"
|
|
||||||
#- cmd: appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
|
||||||
#- cmd: appveyor DownloadFile https://aka.ms/vs/15/release/vs_community.exe
|
|
||||||
#- cmd: vs_community.exe update --wait --quiet --norestart --installPath "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community"
|
|
||||||
#- cmd: ps: .\src\Android\update-android.ps1
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- ps: |
|
|
||||||
if($isWindows) {
|
|
||||||
nuget restore
|
|
||||||
if($env:UPLOAD_KEYSTORE_DEC_SECRET -or$env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
|
||||||
nuget install secure-file -ExcludeVersion
|
|
||||||
}
|
|
||||||
if($env:GOOGLE_SERVICES_DEC_SECRET) {
|
|
||||||
secure-file\tools\secure-file -decrypt src\Android\google-services.json.enc `
|
|
||||||
-secret $env:GOOGLE_SERVICES_DEC_SECRET
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" ]
|
|
||||||
then
|
|
||||||
mkdir dist
|
|
||||||
cp CNAME ./dist
|
|
||||||
cd store
|
|
||||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
|
||||||
mkdir -p temp/fdroid
|
|
||||||
TEMP_DIR="$APPVEYOR_BUILD_FOLDER/store/temp/fdroid"
|
|
||||||
cd fdroid
|
|
||||||
echo "keypass=\"$FDROID_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "keystorepass=\"$FDROID_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
|
||||||
mkdir -p repo
|
|
||||||
curl -Lo repo/com.x8bit.bitwarden-fdroid.apk \
|
|
||||||
https://github.com/bitwarden/mobile/releases/download/$APPVEYOR_REPO_TAG_NAME/com.x8bit.bitwarden-fdroid.apk
|
|
||||||
fdroid update
|
|
||||||
fdroid server update
|
|
||||||
cd ..
|
|
||||||
rm -rf temp/fdroid/archive
|
|
||||||
mv -v temp/fdroid ../dist
|
|
||||||
cd fdroid
|
|
||||||
cp index.html btn.png qr.png ../../dist/fdroid
|
|
||||||
cd $APPVEYOR_BUILD_FOLDER
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:KEYSTORE_DEC_SECRET) {
|
|
||||||
msbuild bitwarden-mobile.sln `
|
|
||||||
"/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
|
|
||||||
"/p:Configuration=Release"
|
|
||||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
|
||||||
.\src\Android\ci-build-apks.ps1
|
|
||||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.aab
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
|
|
||||||
}
|
|
||||||
|
|
||||||
on_success:
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" -a "${GH_TOKEN}" != "" ]
|
|
||||||
then
|
|
||||||
npm run deploy
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:PLAY_DEC_SECRET -and $env:APPVEYOR_REPO_BRANCH -eq 'master') {
|
|
||||||
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
|
|
||||||
cd store\google\Publisher\bin\Release\netcoreapp2.0
|
|
||||||
dotnet Publisher.dll `
|
|
||||||
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
|
|
||||||
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.aab `
|
|
||||||
alpha
|
|
||||||
cd $env:APPVEYOR_BUILD_FOLDER
|
|
||||||
}
|
|
||||||
|
|
||||||
on_finish:
|
|
||||||
- sh: |
|
|
||||||
if [ "${DEBUG_SSH}" == "true" ]
|
|
||||||
then
|
|
||||||
export APPVEYOR_SSH_BLOCK=true
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:DEBUG_RDP -eq "true") {
|
|
||||||
$blockRdp = $true
|
|
||||||
iex ((new-object net.webclient).DownloadString(`
|
|
||||||
'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
|
||||||
}
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
tag: $(APPVEYOR_REPO_TAG_NAME)
|
|
||||||
release: $(RELEASE_NAME)
|
|
||||||
provider: GitHub
|
|
||||||
auth_token: $(GH_TOKEN)
|
|
||||||
artifact: /.*/
|
|
||||||
force_update: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
APPVEYOR_REPO_TAG: true
|
|
||||||
@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
.gitignore = .gitignore
|
.gitignore = .gitignore
|
||||||
appveyor.yml = appveyor.yml
|
|
||||||
.github\workflows\build.yml = .github\workflows\build.yml
|
.github\workflows\build.yml = .github\workflows\build.yml
|
||||||
CONTRIBUTING.md = CONTRIBUTING.md
|
CONTRIBUTING.md = CONTRIBUTING.md
|
||||||
crowdin.yml = crowdin.yml
|
crowdin.yml = crowdin.yml
|
||||||
|
|||||||
@@ -46,14 +46,21 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.chrome.beta", "url_bar"),
|
new Browser("com.chrome.beta", "url_bar"),
|
||||||
new Browser("com.chrome.canary", "url_bar"),
|
new Browser("com.chrome.canary", "url_bar"),
|
||||||
new Browser("com.chrome.dev", "url_bar"),
|
new Browser("com.chrome.dev", "url_bar"),
|
||||||
|
new Browser("com.cookiegames.smartcookie", "search"),
|
||||||
|
new Browser("com.cookiejarapps.android.smartcookieweb", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
||||||
new Browser("com.ecosia.android", "url_bar"),
|
new Browser("com.ecosia.android", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||||
|
new Browser("com.jamal2367.styx", "search"),
|
||||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx", "url_bar"),
|
new Browser("com.microsoft.emmx", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.beta", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.canary", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.dev", "url_bar"),
|
||||||
new Browser("com.mmbox.browser", "search_box"),
|
new Browser("com.mmbox.browser", "search_box"),
|
||||||
new Browser("com.mmbox.xbrowser", "search_box"),
|
new Browser("com.mmbox.xbrowser", "search_box"),
|
||||||
|
new Browser("com.mycompany.app.soulbrowser", "edit_text"),
|
||||||
new Browser("com.naver.whale", "url_bar"),
|
new Browser("com.naver.whale", "url_bar"),
|
||||||
new Browser("com.opera.browser", "url_field"),
|
new Browser("com.opera.browser", "url_field"),
|
||||||
new Browser("com.opera.browser.beta", "url_field"),
|
new Browser("com.opera.browser.beta", "url_field"),
|
||||||
@@ -77,6 +84,10 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("io.github.forkmaintainers.iceraven", "mozac_browser_toolbar_url_view"),
|
new Browser("io.github.forkmaintainers.iceraven", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("mark.via", "am,an"),
|
new Browser("mark.via", "am,an"),
|
||||||
new Browser("mark.via.gp", "as"),
|
new Browser("mark.via.gp", "as"),
|
||||||
|
new Browser("net.slions.fulguris.full.download", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.download.debug", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.playstore", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.playstore.debug", "search"),
|
||||||
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
new Browser("org.bromite.bromite", "url_bar"),
|
new Browser("org.bromite.bromite", "url_bar"),
|
||||||
@@ -182,7 +193,9 @@ namespace Bit.Droid.Accessibility
|
|||||||
new KnownUsernameField("amazon.in", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
new KnownUsernameField("amazon.in", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
new KnownUsernameField("amazon.it", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
new KnownUsernameField("amazon.it", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
new KnownUsernameField("amazon.nl", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
new KnownUsernameField("amazon.nl", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.pl", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
new KnownUsernameField("amazon.sa", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
new KnownUsernameField("amazon.sa", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.se", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
new KnownUsernameField("amazon.sg", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
new KnownUsernameField("amazon.sg", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
|
||||||
// Amazon Web Services
|
// Amazon Web Services
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
<DefineConstants>DEBUG;</DefineConstants>
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>3</WarningLevel>
|
<WarningLevel>3</WarningLevel>
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
|
||||||
<AndroidSupportedAbis />
|
<AndroidSupportedAbis />
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -75,23 +74,26 @@
|
|||||||
<Version>2.1.0.4</Version>
|
<Version>2.1.0.4</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Portable.BouncyCastle">
|
<PackageReference Include="Portable.BouncyCastle">
|
||||||
<Version>1.8.6.7</Version>
|
<Version>1.8.10</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.3-beta01" />
|
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.5.0" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AppCompat.AppCompatResources" Version="1.3.0" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.0" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.6" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.8" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.7" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.4" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.5.3.2</Version>
|
<Version>1.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>71.1740.0</Version>
|
<Version>122.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.3.0.1" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" />
|
<PackageReference Include="Xamarin.Google.Dagger" Version="2.37.0" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" />
|
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||||
<Version>71.1600.0</Version>
|
<Version>117.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -113,25 +115,22 @@
|
|||||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
<Compile Include="Effects\FixedSizeEffect.cs" />
|
||||||
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
||||||
<Compile Include="Effects\TabBarEffect.cs" />
|
<Compile Include="Effects\TabBarEffect.cs" />
|
||||||
<Compile Include="Push\FirebaseInstanceIdService.cs" />
|
|
||||||
<Compile Include="Push\FirebaseMessagingService.cs" />
|
<Compile Include="Push\FirebaseMessagingService.cs" />
|
||||||
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
||||||
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
||||||
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
||||||
<Compile Include="Receivers\LockAlarmReceiver.cs" />
|
|
||||||
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
||||||
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
|
<Compile Include="Renderers\ExtendedGridRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
|
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
|
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomEditorRenderer.cs" />
|
<Compile Include="Renderers\CustomEditorRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomPickerRenderer.cs" />
|
<Compile Include="Renderers\CustomPickerRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomEntryRenderer.cs" />
|
<Compile Include="Renderers\CustomEntryRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomSearchBarRenderer.cs" />
|
<Compile Include="Renderers\CustomSearchBarRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedListViewRenderer.cs" />
|
|
||||||
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
|
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
|
||||||
<Compile Include="Renderers\SendViewCellRenderer.cs" />
|
|
||||||
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
||||||
<Compile Include="Services\AndroidLogService.cs" />
|
<Compile Include="Services\AndroidLogService.cs" />
|
||||||
<Compile Include="MainApplication.cs" />
|
<Compile Include="MainApplication.cs" />
|
||||||
@@ -154,7 +153,6 @@
|
|||||||
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
|
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
|
||||||
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
|
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
|
||||||
<None Include="8bit.keystore.enc" />
|
<None Include="8bit.keystore.enc" />
|
||||||
<None Include="ci-build-apks.ps1" />
|
|
||||||
<GoogleServicesJson Include="google-services.json" />
|
<GoogleServicesJson Include="google-services.json" />
|
||||||
<GoogleServicesJson Include="google-services.json.enc" />
|
<GoogleServicesJson Include="google-services.json.enc" />
|
||||||
<None Include="fdroid-keystore.jks.enc" />
|
<None Include="fdroid-keystore.jks.enc" />
|
||||||
@@ -174,6 +172,9 @@
|
|||||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\id.xml" />
|
<AndroidResource Include="Resources\drawable\id.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\info.xml" />
|
<AndroidResource Include="Resources\drawable\info.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\list_item_bg_dark.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\list_item_bg_nord.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\lock.xml" />
|
<AndroidResource Include="Resources\drawable\lock.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\login.xml" />
|
<AndroidResource Include="Resources\drawable\login.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\logo.xml" />
|
<AndroidResource Include="Resources\drawable\logo.xml" />
|
||||||
@@ -186,7 +187,6 @@
|
|||||||
<AndroidResource Include="Resources\drawable\shield.xml" />
|
<AndroidResource Include="Resources\drawable\shield.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
||||||
<AndroidResource Include="Resources\layout\SendViewCell.axml" />
|
|
||||||
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
||||||
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||||
@@ -263,12 +263,6 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\layout\CipherViewCell.axml">
|
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</AndroidResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\xml\app_restrictions.xml">
|
<AndroidResource Include="Resources\xml\app_restrictions.xml">
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
|||||||
@@ -63,13 +63,20 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.chrome.beta",
|
"com.chrome.beta",
|
||||||
"com.chrome.canary",
|
"com.chrome.canary",
|
||||||
"com.chrome.dev",
|
"com.chrome.dev",
|
||||||
|
"com.cookiegames.smartcookie",
|
||||||
|
"com.cookiejarapps.android.smartcookieweb",
|
||||||
"com.ecosia.android",
|
"com.ecosia.android",
|
||||||
"com.google.android.apps.chrome",
|
"com.google.android.apps.chrome",
|
||||||
"com.google.android.apps.chrome_dev",
|
"com.google.android.apps.chrome_dev",
|
||||||
|
"com.jamal2367.styx",
|
||||||
"com.kiwibrowser.browser",
|
"com.kiwibrowser.browser",
|
||||||
"com.microsoft.emmx",
|
"com.microsoft.emmx",
|
||||||
|
"com.microsoft.emmx.beta",
|
||||||
|
"com.microsoft.emmx.canary",
|
||||||
|
"com.microsoft.emmx.dev",
|
||||||
"com.mmbox.browser",
|
"com.mmbox.browser",
|
||||||
"com.mmbox.xbrowser",
|
"com.mmbox.xbrowser",
|
||||||
|
"com.mycompany.app.soulbrowser",
|
||||||
"com.naver.whale",
|
"com.naver.whale",
|
||||||
"com.opera.browser",
|
"com.opera.browser",
|
||||||
"com.opera.browser.beta",
|
"com.opera.browser.beta",
|
||||||
@@ -92,6 +99,10 @@ namespace Bit.Droid.Autofill
|
|||||||
"io.github.forkmaintainers.iceraven",
|
"io.github.forkmaintainers.iceraven",
|
||||||
"mark.via",
|
"mark.via",
|
||||||
"mark.via.gp",
|
"mark.via.gp",
|
||||||
|
"net.slions.fulguris.full.download",
|
||||||
|
"net.slions.fulguris.full.download.debug",
|
||||||
|
"net.slions.fulguris.full.playstore",
|
||||||
|
"net.slions.fulguris.full.playstore.debug",
|
||||||
"org.adblockplus.browser",
|
"org.adblockplus.browser",
|
||||||
"org.adblockplus.browser.beta",
|
"org.adblockplus.browser.beta",
|
||||||
"org.bromite.bromite",
|
"org.bromite.bromite",
|
||||||
@@ -131,13 +142,14 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
var allCiphers = ciphers.Item1.ToList();
|
var allCiphers = ciphers.Item1.ToList();
|
||||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||||
return allCiphers.Select(c => new FilledItem(c)).ToList();
|
var nonPromptCiphers = allCiphers.Where(cipher => cipher.Reprompt == CipherRepromptType.None);
|
||||||
|
return nonPromptCiphers.Select(c => new FilledItem(c)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parser.FieldCollection.FillableForCard)
|
else if (parser.FieldCollection.FillableForCard)
|
||||||
{
|
{
|
||||||
var ciphers = await cipherService.GetAllDecryptedAsync();
|
var ciphers = await cipherService.GetAllDecryptedAsync();
|
||||||
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
|
return ciphers.Where(c => c.Type == CipherType.Card && c.Reprompt == CipherRepromptType.None).Select(c => new FilledItem(c)).ToList();
|
||||||
}
|
}
|
||||||
return new List<FilledItem>();
|
return new List<FilledItem>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<FilledItem> items = null;
|
List<FilledItem> items = null;
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
var locked = await _vaultTimeoutService.IsLockedAsync();
|
var locked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!locked)
|
if (!locked)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,9 +27,20 @@ namespace Bit.Droid
|
|||||||
Icon = "@mipmap/ic_launcher",
|
Icon = "@mipmap/ic_launcher",
|
||||||
Theme = "@style/LaunchTheme",
|
Theme = "@style/LaunchTheme",
|
||||||
MainLauncher = true,
|
MainLauncher = true,
|
||||||
|
LaunchMode = LaunchMode.SingleTask,
|
||||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
||||||
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
||||||
ConfigChanges.Navigation)]
|
ConfigChanges.Navigation)]
|
||||||
|
[IntentFilter(
|
||||||
|
new[] { Intent.ActionSend },
|
||||||
|
Categories = new[] { Intent.CategoryDefault },
|
||||||
|
DataMimeTypes = new[]
|
||||||
|
{
|
||||||
|
@"application/*",
|
||||||
|
@"image/*",
|
||||||
|
@"video/*",
|
||||||
|
@"text/*"
|
||||||
|
})]
|
||||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
@@ -40,7 +51,6 @@ namespace Bit.Droid
|
|||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _vaultTimeoutAlarmPendingIntent;
|
|
||||||
private PendingIntent _clearClipboardPendingIntent;
|
private PendingIntent _clearClipboardPendingIntent;
|
||||||
private PendingIntent _eventUploadPendingIntent;
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
@@ -53,9 +63,6 @@ namespace Bit.Droid
|
|||||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
|
||||||
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
|
||||||
PendingIntentFlags.UpdateCurrent);
|
|
||||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
@@ -93,20 +100,7 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "scheduleVaultTimeoutTimer")
|
if (message.Command == "startEventTimer")
|
||||||
{
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
var vaultTimeoutMinutes = (int)message.Data;
|
|
||||||
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
|
|
||||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
|
|
||||||
}
|
|
||||||
else if (message.Command == "cancelVaultTimeoutTimer")
|
|
||||||
{
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
|
|
||||||
}
|
|
||||||
else if (message.Command == "startEventTimer")
|
|
||||||
{
|
{
|
||||||
StartEventAlarm();
|
StartEventAlarm();
|
||||||
}
|
}
|
||||||
@@ -161,25 +155,40 @@ namespace Bit.Droid
|
|||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
{
|
{
|
||||||
base.OnNewIntent(intent);
|
base.OnNewIntent(intent);
|
||||||
if (intent.GetBooleanExtra("generatorTile", false))
|
try
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
if (intent.GetBooleanExtra("generatorTile", false))
|
||||||
if (_appOptions != null)
|
|
||||||
{
|
{
|
||||||
_appOptions.GeneratorTile = true;
|
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.GeneratorTile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intent.GetBooleanExtra("myVaultTile", false))
|
||||||
|
{
|
||||||
|
_messagingService.Send("popAllAndGoToTabMyVault");
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.MyVaultTile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||||
|
{
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.CreateSend = GetCreateSendRequest(intent);
|
||||||
|
}
|
||||||
|
_messagingService.Send("popAllAndGoToTabSend");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParseYubiKey(intent.DataString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (intent.GetBooleanExtra("myVaultTile", false))
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabMyVault");
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
||||||
if (_appOptions != null)
|
|
||||||
{
|
|
||||||
_appOptions.MyVaultTile = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParseYubiKey(intent.DataString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +307,8 @@ namespace Bit.Droid
|
|||||||
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
||||||
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
||||||
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
||||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false),
|
||||||
|
CreateSend = GetCreateSendRequest(Intent)
|
||||||
};
|
};
|
||||||
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||||
if (fillType > 0)
|
if (fillType > 0)
|
||||||
@@ -320,6 +330,42 @@ namespace Bit.Droid
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Tuple<SendType, string, byte[], string> GetCreateSendRequest(Intent intent)
|
||||||
|
{
|
||||||
|
if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||||
|
{
|
||||||
|
if ((intent.Flags & ActivityFlags.LaunchedFromHistory) == ActivityFlags.LaunchedFromHistory)
|
||||||
|
{
|
||||||
|
// don't re-deliver intent if resuming from app switcher
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var type = intent.Type;
|
||||||
|
if (type.Contains("text/"))
|
||||||
|
{
|
||||||
|
var subject = intent.GetStringExtra(Intent.ExtraSubject);
|
||||||
|
var text = intent.GetStringExtra(Intent.ExtraText);
|
||||||
|
return new Tuple<SendType, string, byte[], string>(SendType.Text, subject, null, text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var data = intent.ClipData?.GetItemAt(0);
|
||||||
|
var uri = data?.Uri;
|
||||||
|
var filename = AndroidHelpers.GetFileName(ApplicationContext, uri);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
stream.CopyTo(memoryStream);
|
||||||
|
return new Tuple<SendType, string, byte[], string>(SendType.File, filename, memoryStream.ToArray(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Java.IO.FileNotFoundException) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void ParseYubiKey(string data)
|
private void ParseYubiKey(string data)
|
||||||
{
|
{
|
||||||
if (data == null)
|
if (data == null)
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ namespace Bit.Droid
|
|||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
var biometricService = new BiometricService();
|
var biometricService = new BiometricService();
|
||||||
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
|
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
|
||||||
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
||||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||||
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
|
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
|
||||||
@@ -110,6 +113,9 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||||
|
ServiceContainer.Register<ICryptoFunctionService>("cryptoFunctionService", cryptoFunctionService);
|
||||||
|
ServiceContainer.Register<ICryptoService>("cryptoService", cryptoService);
|
||||||
|
ServiceContainer.Register<IPasswordRepromptService>("passwordRepromptService", passwordRepromptService);
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
#if FDROID
|
#if FDROID
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="2.9.1"
|
android:versionName="2.11.2"
|
||||||
android:installLocation="internalOnly"
|
android:installLocation="internalOnly"
|
||||||
package="com.x8bit.bitwarden">
|
package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
@@ -40,20 +40,6 @@
|
|||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
|
|
||||||
android:exported="false" />
|
|
||||||
<receiver
|
|
||||||
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="com.google.android.c2dm.permission.SEND">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
|
||||||
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
|
|
||||||
<category android:name="com.x8bit.bitwarden" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||||
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
||||||
|
|
||||||
@@ -68,12 +54,7 @@
|
|||||||
<!-- Package visibility (for Android 11+) -->
|
<!-- Package visibility (for Android 11+) -->
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="*"/>
|
||||||
<data android:scheme="http"/>
|
|
||||||
</intent>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<data android:scheme="https"/>
|
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#if !FDROID
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Firebase.Iid;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Push
|
|
||||||
{
|
|
||||||
[Service]
|
|
||||||
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
|
|
||||||
public class FirebaseInstanceIdService : Firebase.Iid.FirebaseInstanceIdService
|
|
||||||
{
|
|
||||||
public async override void OnTokenRefresh()
|
|
||||||
{
|
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
|
||||||
await storageService.SaveAsync(Constants.PushRegisteredTokenKey, FirebaseInstanceId.Instance.Token);
|
|
||||||
await pushNotificationService.RegisterAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Firebase.Messaging;
|
using Firebase.Messaging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -10,10 +10,19 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.Droid.Push
|
namespace Bit.Droid.Push
|
||||||
{
|
{
|
||||||
[Service]
|
[Service(Exported=false)]
|
||||||
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
|
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
|
||||||
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
|
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
|
||||||
{
|
{
|
||||||
|
public async override void OnNewToken(string token)
|
||||||
|
{
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
|
|
||||||
|
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
||||||
|
await pushNotificationService.RegisterAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async override void OnMessageReceived(RemoteMessage message)
|
public async override void OnMessageReceived(RemoteMessage message)
|
||||||
{
|
{
|
||||||
if (message?.Data == null)
|
if (message?.Data == null)
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using Android.Content;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Receivers
|
|
||||||
{
|
|
||||||
[BroadcastReceiver(Name = "com.x8bit.bitwarden.LockAlarmReceiver", Exported = false)]
|
|
||||||
public class LockAlarmReceiver : BroadcastReceiver
|
|
||||||
{
|
|
||||||
public async override void OnReceive(Context context, Intent intent)
|
|
||||||
{
|
|
||||||
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
|
||||||
await vaultTimeoutService.CheckVaultTimeoutAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.Graphics;
|
|
||||||
using Android.Runtime;
|
|
||||||
using Android.Util;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Views.InputMethods;
|
|
||||||
using Android.Widget;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using FFImageLoading;
|
|
||||||
using FFImageLoading.Views;
|
|
||||||
using FFImageLoading.Work;
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(CipherViewCell), typeof(CipherViewCellRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class CipherViewCellRenderer : ViewCellRenderer
|
|
||||||
{
|
|
||||||
private static Typeface _faTypeface;
|
|
||||||
private static Typeface _miTypeface;
|
|
||||||
private static Android.Graphics.Color _textColor;
|
|
||||||
private static Android.Graphics.Color _mutedColor;
|
|
||||||
private static Android.Graphics.Color _disabledIconColor;
|
|
||||||
private static bool _usingLightTheme;
|
|
||||||
|
|
||||||
private AndroidCipherCell _cell;
|
|
||||||
|
|
||||||
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
|
|
||||||
ViewGroup parent, Context context)
|
|
||||||
{
|
|
||||||
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
|
|
||||||
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
|
|
||||||
if (_faTypeface == null)
|
|
||||||
{
|
|
||||||
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
|
|
||||||
}
|
|
||||||
if (_miTypeface == null)
|
|
||||||
{
|
|
||||||
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
|
|
||||||
}
|
|
||||||
if (_textColor == default(Android.Graphics.Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_mutedColor == default(Android.Graphics.Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_disabledIconColor == default(Android.Graphics.Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
|
|
||||||
}
|
|
||||||
_usingLightTheme = ThemeManager.UsingLightTheme;
|
|
||||||
|
|
||||||
var cipherCell = item as CipherViewCell;
|
|
||||||
_cell = convertView as AndroidCipherCell;
|
|
||||||
if (_cell == null)
|
|
||||||
{
|
|
||||||
_cell = new AndroidCipherCell(context, cipherCell, _faTypeface, _miTypeface);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_cell.CipherViewCell.PropertyChanged -= CellPropertyChanged;
|
|
||||||
}
|
|
||||||
cipherCell.PropertyChanged += CellPropertyChanged;
|
|
||||||
_cell.UpdateCell(cipherCell);
|
|
||||||
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
|
|
||||||
return _cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var cipherCell = sender as CipherViewCell;
|
|
||||||
_cell.CipherViewCell = cipherCell;
|
|
||||||
if (e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
|
|
||||||
{
|
|
||||||
_cell.UpdateCell(cipherCell);
|
|
||||||
}
|
|
||||||
else if (e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
|
|
||||||
{
|
|
||||||
_cell.UpdateIconImage(cipherCell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AndroidCipherCell : LinearLayout, INativeElementView
|
|
||||||
{
|
|
||||||
private readonly Typeface _faTypeface;
|
|
||||||
private readonly Typeface _miTypeface;
|
|
||||||
|
|
||||||
private IScheduledWork _currentTask;
|
|
||||||
|
|
||||||
public AndroidCipherCell(Context context, CipherViewCell cipherView, Typeface faTypeface, Typeface miTypeface)
|
|
||||||
: base(context)
|
|
||||||
{
|
|
||||||
CipherViewCell = cipherView;
|
|
||||||
_faTypeface = faTypeface;
|
|
||||||
_miTypeface = miTypeface;
|
|
||||||
|
|
||||||
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.CipherViewCell, null);
|
|
||||||
IconImage = view.FindViewById<IconImageView>(Resource.Id.CipherCellIconImage);
|
|
||||||
Icon = view.FindViewById<TextView>(Resource.Id.CipherCellIcon);
|
|
||||||
Name = view.FindViewById<TextView>(Resource.Id.CipherCellName);
|
|
||||||
SubTitle = view.FindViewById<TextView>(Resource.Id.CipherCellSubTitle);
|
|
||||||
SharedIcon = view.FindViewById<TextView>(Resource.Id.CipherCellSharedIcon);
|
|
||||||
AttachmentsIcon = view.FindViewById<TextView>(Resource.Id.CipherCellAttachmentsIcon);
|
|
||||||
MoreButton = view.FindViewById<Android.Widget.Button>(Resource.Id.CipherCellButton);
|
|
||||||
MoreButton.Click += MoreButton_Click;
|
|
||||||
|
|
||||||
Icon.Typeface = _faTypeface;
|
|
||||||
SharedIcon.Typeface = _faTypeface;
|
|
||||||
AttachmentsIcon.Typeface = _faTypeface;
|
|
||||||
MoreButton.Typeface = _miTypeface;
|
|
||||||
|
|
||||||
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
|
|
||||||
Icon.SetTextSize(ComplexUnitType.Pt, 10);
|
|
||||||
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
|
|
||||||
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
SharedIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
AttachmentsIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
|
|
||||||
|
|
||||||
AddView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CipherViewCell CipherViewCell { get; set; }
|
|
||||||
public Element Element => CipherViewCell;
|
|
||||||
|
|
||||||
public IconImageView IconImage { get; set; }
|
|
||||||
public TextView Icon { get; set; }
|
|
||||||
public TextView Name { get; set; }
|
|
||||||
public TextView SubTitle { get; set; }
|
|
||||||
public TextView SharedIcon { get; set; }
|
|
||||||
public TextView AttachmentsIcon { get; set; }
|
|
||||||
public Android.Widget.Button MoreButton { get; set; }
|
|
||||||
|
|
||||||
public void UpdateCell(CipherViewCell cipherCell)
|
|
||||||
{
|
|
||||||
UpdateIconImage(cipherCell);
|
|
||||||
|
|
||||||
var cipher = cipherCell.Cipher;
|
|
||||||
Name.Text = cipher.Name;
|
|
||||||
if (!string.IsNullOrWhiteSpace(cipher.SubTitle))
|
|
||||||
{
|
|
||||||
SubTitle.Text = cipher.SubTitle;
|
|
||||||
SubTitle.Visibility = ViewStates.Visible;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SubTitle.Visibility = ViewStates.Invisible;
|
|
||||||
}
|
|
||||||
SharedIcon.Visibility = cipher.Shared ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
AttachmentsIcon.Visibility = cipher.HasAttachments ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateIconImage(CipherViewCell cipherCell)
|
|
||||||
{
|
|
||||||
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
|
|
||||||
{
|
|
||||||
_currentTask.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
var cipher = cipherCell.Cipher;
|
|
||||||
|
|
||||||
var iconImage = cipherCell.GetIconImage(cipher);
|
|
||||||
if (iconImage.Item2 != null)
|
|
||||||
{
|
|
||||||
IconImage.SetImageResource(Resource.Drawable.login);
|
|
||||||
IconImage.Visibility = ViewStates.Visible;
|
|
||||||
Icon.Visibility = ViewStates.Gone;
|
|
||||||
_currentTask = ImageService.Instance.LoadUrl(iconImage.Item2).DownSample(64).Into(IconImage);
|
|
||||||
IconImage.Key = iconImage.Item2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IconImage.Visibility = ViewStates.Gone;
|
|
||||||
Icon.Visibility = ViewStates.Visible;
|
|
||||||
Icon.Text = iconImage.Item1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateColors(Android.Graphics.Color textColor, Android.Graphics.Color mutedColor,
|
|
||||||
Android.Graphics.Color iconDisabledColor)
|
|
||||||
{
|
|
||||||
Name.SetTextColor(textColor);
|
|
||||||
SubTitle.SetTextColor(mutedColor);
|
|
||||||
Icon.SetTextColor(mutedColor);
|
|
||||||
SharedIcon.SetTextColor(mutedColor);
|
|
||||||
AttachmentsIcon.SetTextColor(mutedColor);
|
|
||||||
MoreButton.SetTextColor(iconDisabledColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoreButton_Click(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
|
|
||||||
{
|
|
||||||
CipherViewCell.ButtonCommand.Execute(CipherViewCell.Cipher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
MoreButton.Click -= MoreButton_Click;
|
|
||||||
}
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Android.Runtime.Preserve(AllMembers = true)]
|
|
||||||
[Register("bit.droid.renderers.IconImageView")]
|
|
||||||
public class IconImageView : ImageViewAsync
|
|
||||||
{
|
|
||||||
public IconImageView(Context context) : base(context)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public IconImageView(IntPtr javaReference, JniHandleOwnership transfer)
|
|
||||||
: base(javaReference, transfer)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public IconImageView(Context context, IAttributeSet attrs)
|
|
||||||
: base(context, attrs)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public string Key { get; set; }
|
|
||||||
|
|
||||||
protected override void JavaFinalize()
|
|
||||||
{
|
|
||||||
SetImageDrawable(null);
|
|
||||||
SetImageBitmap(null);
|
|
||||||
ImageService.Instance.InvalidateCacheEntryAsync(Key, FFImageLoading.Cache.CacheType.Memory);
|
|
||||||
base.JavaFinalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
43
src/Android/Renderers/ExtendedGridRenderer.cs
Normal file
43
src/Android/Renderers/ExtendedGridRenderer.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ExtendedGrid), typeof(ExtendedGridRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedGridRenderer : ViewRenderer
|
||||||
|
{
|
||||||
|
private static int? _bgResId;
|
||||||
|
|
||||||
|
public ExtendedGridRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(elementChangedEvent);
|
||||||
|
if (elementChangedEvent.NewElement != null)
|
||||||
|
{
|
||||||
|
SetBackgroundResource(GetBgResId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBgResId()
|
||||||
|
{
|
||||||
|
if (_bgResId == null)
|
||||||
|
{
|
||||||
|
if (ThemeManager.GetTheme(true) == "nord")
|
||||||
|
{
|
||||||
|
_bgResId = Resource.Drawable.list_item_bg_nord;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
|
||||||
|
Resource.Drawable.list_item_bg_dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _bgResId.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Android.Content;
|
|
||||||
using Android.Views;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(ExtendedListViewRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class ExtendedListViewRenderer : ListViewRenderer
|
|
||||||
{
|
|
||||||
public ExtendedListViewRenderer(Context context)
|
|
||||||
: base(context)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
|
||||||
{
|
|
||||||
base.OnElementChanged(e);
|
|
||||||
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
|
|
||||||
{
|
|
||||||
// Pad for FAB
|
|
||||||
Control.SetPadding(0, 0, 0, 170);
|
|
||||||
Control.SetClipToPadding(false);
|
|
||||||
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
43
src/Android/Renderers/ExtendedStackLayoutRenderer.cs
Normal file
43
src/Android/Renderers/ExtendedStackLayoutRenderer.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ExtendedStackLayout), typeof(ExtendedStackLayoutRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedStackLayoutRenderer : ViewRenderer
|
||||||
|
{
|
||||||
|
private static int? _bgResId;
|
||||||
|
|
||||||
|
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(elementChangedEvent);
|
||||||
|
if (elementChangedEvent.NewElement != null)
|
||||||
|
{
|
||||||
|
SetBackgroundResource(GetBgResId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBgResId()
|
||||||
|
{
|
||||||
|
if (_bgResId == null)
|
||||||
|
{
|
||||||
|
if (ThemeManager.GetTheme(true) == "nord")
|
||||||
|
{
|
||||||
|
_bgResId = Resource.Drawable.list_item_bg_nord;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
|
||||||
|
Resource.Drawable.list_item_bg_dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _bgResId.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.Graphics;
|
|
||||||
using Android.Util;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Widget;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using FFImageLoading.Work;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
using Button = Android.Widget.Button;
|
|
||||||
using Color = Android.Graphics.Color;
|
|
||||||
using View = Android.Views.View;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(SendViewCell), typeof(SendViewCellRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class SendViewCellRenderer : ViewCellRenderer
|
|
||||||
{
|
|
||||||
private static Typeface _faTypeface;
|
|
||||||
private static Typeface _miTypeface;
|
|
||||||
private static Color _textColor;
|
|
||||||
private static Color _mutedColor;
|
|
||||||
private static Color _disabledIconColor;
|
|
||||||
private static bool _usingLightTheme;
|
|
||||||
|
|
||||||
private AndroidSendCell _cell;
|
|
||||||
|
|
||||||
protected override View GetCellCore(Cell item, View convertView,
|
|
||||||
ViewGroup parent, Context context)
|
|
||||||
{
|
|
||||||
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
|
|
||||||
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
|
|
||||||
if (_faTypeface == null)
|
|
||||||
{
|
|
||||||
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
|
|
||||||
}
|
|
||||||
if (_miTypeface == null)
|
|
||||||
{
|
|
||||||
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
|
|
||||||
}
|
|
||||||
if (_textColor == default(Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_mutedColor == default(Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_disabledIconColor == default(Color) || themeChanged)
|
|
||||||
{
|
|
||||||
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
|
|
||||||
}
|
|
||||||
_usingLightTheme = ThemeManager.UsingLightTheme;
|
|
||||||
|
|
||||||
var sendCell = item as SendViewCell;
|
|
||||||
_cell = convertView as AndroidSendCell;
|
|
||||||
if (_cell == null)
|
|
||||||
{
|
|
||||||
_cell = new AndroidSendCell(context, sendCell, _faTypeface, _miTypeface);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_cell.SendViewCell.PropertyChanged -= CellPropertyChanged;
|
|
||||||
}
|
|
||||||
sendCell.PropertyChanged += CellPropertyChanged;
|
|
||||||
_cell.UpdateCell(sendCell);
|
|
||||||
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
|
|
||||||
return _cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
var sendCell = sender as SendViewCell;
|
|
||||||
_cell.SendViewCell = sendCell;
|
|
||||||
if (e.PropertyName == SendViewCell.SendProperty.PropertyName)
|
|
||||||
{
|
|
||||||
_cell.UpdateCell(sendCell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AndroidSendCell : LinearLayout, INativeElementView
|
|
||||||
{
|
|
||||||
private readonly Typeface _faTypeface;
|
|
||||||
private readonly Typeface _miTypeface;
|
|
||||||
|
|
||||||
private IScheduledWork _currentTask;
|
|
||||||
|
|
||||||
public AndroidSendCell(Context context, SendViewCell sendView, Typeface faTypeface, Typeface miTypeface)
|
|
||||||
: base(context)
|
|
||||||
{
|
|
||||||
SendViewCell = sendView;
|
|
||||||
_faTypeface = faTypeface;
|
|
||||||
_miTypeface = miTypeface;
|
|
||||||
|
|
||||||
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.SendViewCell, null);
|
|
||||||
Icon = view.FindViewById<TextView>(Resource.Id.SendCellIcon);
|
|
||||||
Name = view.FindViewById<TextView>(Resource.Id.SendCellName);
|
|
||||||
SubTitle = view.FindViewById<TextView>(Resource.Id.SendCellSubTitle);
|
|
||||||
DisabledIcon = view.FindViewById<TextView>(Resource.Id.SendCellDisabledIcon);
|
|
||||||
HasPasswordIcon = view.FindViewById<TextView>(Resource.Id.SendCellHasPasswordIcon);
|
|
||||||
MaxAccessCountReachedIcon = view.FindViewById<TextView>(Resource.Id.SendCellMaxAccessCountReachedIcon);
|
|
||||||
ExpiredIcon = view.FindViewById<TextView>(Resource.Id.SendCellExpiredIcon);
|
|
||||||
PendingDeleteIcon = view.FindViewById<TextView>(Resource.Id.SendCellPendingDeleteIcon);
|
|
||||||
MoreButton = view.FindViewById<Button>(Resource.Id.SendCellButton);
|
|
||||||
MoreButton.Click += MoreButton_Click;
|
|
||||||
|
|
||||||
Icon.Typeface = _faTypeface;
|
|
||||||
DisabledIcon.Typeface = _faTypeface;
|
|
||||||
HasPasswordIcon.Typeface = _faTypeface;
|
|
||||||
MaxAccessCountReachedIcon.Typeface = _faTypeface;
|
|
||||||
ExpiredIcon.Typeface = _faTypeface;
|
|
||||||
PendingDeleteIcon.Typeface = _faTypeface;
|
|
||||||
MoreButton.Typeface = _miTypeface;
|
|
||||||
|
|
||||||
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
|
|
||||||
Icon.SetTextSize(ComplexUnitType.Pt, 10);
|
|
||||||
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
|
|
||||||
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
DisabledIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
HasPasswordIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
MaxAccessCountReachedIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
ExpiredIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
PendingDeleteIcon.SetTextSize(ComplexUnitType.Sp, small);
|
|
||||||
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
|
|
||||||
|
|
||||||
if (!SendViewCell.ShowOptions)
|
|
||||||
{
|
|
||||||
MoreButton.Visibility = ViewStates.Gone;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SendViewCell SendViewCell { get; set; }
|
|
||||||
public Element Element => SendViewCell;
|
|
||||||
|
|
||||||
public TextView Icon { get; set; }
|
|
||||||
public TextView Name { get; set; }
|
|
||||||
public TextView SubTitle { get; set; }
|
|
||||||
public TextView DisabledIcon { get; set; }
|
|
||||||
public TextView HasPasswordIcon { get; set; }
|
|
||||||
public TextView MaxAccessCountReachedIcon { get; set; }
|
|
||||||
public TextView ExpiredIcon { get; set; }
|
|
||||||
public TextView PendingDeleteIcon { get; set; }
|
|
||||||
public Button MoreButton { get; set; }
|
|
||||||
|
|
||||||
public void UpdateCell(SendViewCell sendCell)
|
|
||||||
{
|
|
||||||
UpdateIconImage(sendCell);
|
|
||||||
|
|
||||||
var send = sendCell.Send;
|
|
||||||
Name.Text = send.Name;
|
|
||||||
SubTitle.Text = send.DisplayDate;
|
|
||||||
DisabledIcon.Visibility = send.Disabled ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
HasPasswordIcon.Visibility = send.HasPassword ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
MaxAccessCountReachedIcon.Visibility = send.MaxAccessCountReached ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
ExpiredIcon.Visibility = send.Expired ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
PendingDeleteIcon.Visibility = send.PendingDelete ? ViewStates.Visible : ViewStates.Gone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateIconImage(SendViewCell sendCell)
|
|
||||||
{
|
|
||||||
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
|
|
||||||
{
|
|
||||||
_currentTask.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
var send = sendCell.Send;
|
|
||||||
var iconImage = sendCell.GetIconImage(send);
|
|
||||||
Icon.Text = iconImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateColors(Color textColor, Color mutedColor,
|
|
||||||
Color iconDisabledColor)
|
|
||||||
{
|
|
||||||
Name.SetTextColor(textColor);
|
|
||||||
SubTitle.SetTextColor(mutedColor);
|
|
||||||
Icon.SetTextColor(mutedColor);
|
|
||||||
DisabledIcon.SetTextColor(mutedColor);
|
|
||||||
HasPasswordIcon.SetTextColor(mutedColor);
|
|
||||||
MaxAccessCountReachedIcon.SetTextColor(mutedColor);
|
|
||||||
ExpiredIcon.SetTextColor(mutedColor);
|
|
||||||
PendingDeleteIcon.SetTextColor(mutedColor);
|
|
||||||
MoreButton.SetTextColor(iconDisabledColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoreButton_Click(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (SendViewCell.ButtonCommand?.CanExecute(SendViewCell.Send) ?? false)
|
|
||||||
{
|
|
||||||
SendViewCell.ButtonCommand.Execute(SendViewCell.Send);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
MoreButton.Click -= MoreButton_Click;
|
|
||||||
}
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
src/Android/Resources/drawable/list_item_bg.xml
Normal file
7
src/Android/Resources/drawable/list_item_bg.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/itemPressed">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@android:id/mask"
|
||||||
|
android:drawable="@android:color/white" />
|
||||||
|
</ripple>
|
||||||
7
src/Android/Resources/drawable/list_item_bg_dark.xml
Normal file
7
src/Android/Resources/drawable/list_item_bg_dark.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/dark_primary">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@android:id/mask"
|
||||||
|
android:drawable="@android:color/white" />
|
||||||
|
</ripple>
|
||||||
7
src/Android/Resources/drawable/list_item_bg_nord.xml
Normal file
7
src/Android/Resources/drawable/list_item_bg_nord.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/nord_primary">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@android:id/mask"
|
||||||
|
android:drawable="@android:color/white" />
|
||||||
|
</ripple>
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:minHeight="44dp"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingLeft="2.2dp">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="39.8dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="center">
|
|
||||||
<bit.droid.renderers.IconImageView
|
|
||||||
android:id="@+id/CipherCellIconImage"
|
|
||||||
android:layout_width="22dp"
|
|
||||||
android:layout_height="22dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/CipherCellIcon"
|
|
||||||
android:layout_width="26dp"
|
|
||||||
android:layout_height="26dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="[X]" />
|
|
||||||
</LinearLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/CipherCellContent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingVertical="7.65dp">
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/CipherCellContentTop"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/CipherCellName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:text="Name" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/CipherCellSharedIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/CipherCellAttachmentsIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
</LinearLayout>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/CipherCellSubTitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:text="SubTitle" />
|
|
||||||
</LinearLayout>
|
|
||||||
<Button
|
|
||||||
android:id="@+id/CipherCellButton"
|
|
||||||
android:layout_width="37dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text=""
|
|
||||||
android:gravity="center"
|
|
||||||
android:padding="0dp"
|
|
||||||
android:background="@android:color/transparent" />
|
|
||||||
</LinearLayout>
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:minHeight="44dp"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingLeft="2.2dp">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="39.8dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="center">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellIcon"
|
|
||||||
android:layout_width="26dp"
|
|
||||||
android:layout_height="26dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="[X]" />
|
|
||||||
</LinearLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/SendCellContent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingVertical="7.65dp">
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/SendCellContentTop"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:text="Name" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellDisabledIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellHasPasswordIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellMaxAccessCountReachedIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellExpiredIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellPendingDeleteIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:paddingLeft="5dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="" />
|
|
||||||
</LinearLayout>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/SendCellSubTitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:text="SubTitle" />
|
|
||||||
</LinearLayout>
|
|
||||||
<Button
|
|
||||||
android:id="@+id/SendCellButton"
|
|
||||||
android:layout_width="37dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text=""
|
|
||||||
android:gravity="center"
|
|
||||||
android:padding="0dp"
|
|
||||||
android:background="@android:color/transparent" />
|
|
||||||
</LinearLayout>
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<color name="primary">#175DDC</color>
|
<color name="primary">#175DDC</color>
|
||||||
<color name="notificationBar">#1452BC</color>
|
<color name="notificationBar">#1452BC</color>
|
||||||
<color name="border">#dddddd</color>
|
<color name="border">#dddddd</color>
|
||||||
|
<color name="itemPressed">#bbbbbb</color>
|
||||||
|
|
||||||
<!-- Dark theme -->
|
<!-- Dark theme -->
|
||||||
<color name="dark_primary">#52bdfb</color>
|
<color name="dark_primary">#52bdfb</color>
|
||||||
|
|||||||
@@ -56,6 +56,12 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.chrome.dev"
|
android:name="com.chrome.dev"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.cookiegames.smartcookie"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.cookiejarapps.android.smartcookieweb"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.ecosia.android"
|
android:name="com.ecosia.android"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -65,18 +71,33 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.google.android.apps.chrome_dev"
|
android:name="com.google.android.apps.chrome_dev"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.jamal2367.styx"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.kiwibrowser.browser"
|
android:name="com.kiwibrowser.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.microsoft.emmx"
|
android:name="com.microsoft.emmx"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.microsoft.emmx.beta"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.microsoft.emmx.canary"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.microsoft.emmx.dev"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.mmbox.browser"
|
android:name="com.mmbox.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.mmbox.xbrowser"
|
android:name="com.mmbox.xbrowser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.mycompany.app.soulbrowser"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.naver.whale"
|
android:name="com.naver.whale"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -143,6 +164,18 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="mark.via.gp"
|
android:name="mark.via.gp"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="net.slions.fulguris.full.download"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="net.slions.fulguris.full.download.debug"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="net.slions.fulguris.full.playstore"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="net.slions.fulguris.full.playstore.debug"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.adblockplus.browser"
|
android:name="org.adblockplus.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public Task<string> DisplayPromptAync(string title = null, string description = null,
|
public Task<string> DisplayPromptAync(string title = null, string description = null,
|
||||||
string text = null, string okButtonText = null, string cancelButtonText = null,
|
string text = null, string okButtonText = null, string cancelButtonText = null,
|
||||||
bool numericKeyboard = false, bool autofocus = true)
|
bool numericKeyboard = false, bool autofocus = true, bool password = false)
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
if (activity == null)
|
if (activity == null)
|
||||||
@@ -333,6 +333,10 @@ namespace Bit.Droid.Services
|
|||||||
input.KeyListener = DigitsKeyListener.GetInstance(false, false);
|
input.KeyListener = DigitsKeyListener.GetInstance(false, false);
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
}
|
}
|
||||||
|
if (password)
|
||||||
|
{
|
||||||
|
input.InputType = InputTypes.TextVariationPassword | InputTypes.ClassText;
|
||||||
|
}
|
||||||
|
|
||||||
input.ImeOptions = input.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
|
input.ImeOptions = input.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning |
|
||||||
(ImeAction)ImeFlags.NoExtractUi;
|
(ImeAction)ImeFlags.NoExtractUi;
|
||||||
@@ -761,6 +765,17 @@ namespace Bit.Droid.Services
|
|||||||
return SystemClock.ElapsedRealtime();
|
return SystemClock.ElapsedRealtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CloseMainApp()
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
if (activity == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activity.Finish();
|
||||||
|
_messagingService.Send("finishMainActivity");
|
||||||
|
}
|
||||||
|
|
||||||
private bool DeleteDir(Java.IO.File dir)
|
private bool DeleteDir(Java.IO.File dir)
|
||||||
{
|
{
|
||||||
if (dir != null && dir.IsDirectory)
|
if (dir != null && dir.IsDirectory)
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ using Android.Content.PM;
|
|||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
|
[Activity(
|
||||||
|
NoHistory = true,
|
||||||
|
LaunchMode = LaunchMode.SingleTop)]
|
||||||
[IntentFilter(new[] { Android.Content.Intent.ActionView },
|
[IntentFilter(new[] { Android.Content.Intent.ActionView },
|
||||||
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
|
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
|
||||||
DataScheme = "bitwarden")]
|
DataScheme = "bitwarden")]
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
$rootPath = $env:APPVEYOR_BUILD_FOLDER;
|
|
||||||
|
|
||||||
$androidPath = $($rootPath + "\src\Android\Android.csproj");
|
|
||||||
$appPath = $($rootPath + "\src\App\App.csproj");
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Increment Version"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$androidManifest = $($rootPath + "\src\Android\Properties\AndroidManifest.xml");
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidManifest);
|
|
||||||
|
|
||||||
$node=$xml.SelectNodes("/manifest");
|
|
||||||
$node.SetAttribute("android:versionCode", $env:APPVEYOR_BUILD_NUMBER);
|
|
||||||
|
|
||||||
$xml.Save($androidManifest);
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Decrypt Keystore"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$encKeystorePath = $($rootPath + "\src\Android\8bit.keystore.enc");
|
|
||||||
$encFdroidKeystorePath = $($rootPath + "\src\Android\fdroid-keystore.jks.enc");
|
|
||||||
$encUploadKeystorePath = $($rootPath + "\src\Android\upload-keystore.jks.enc");
|
|
||||||
$secureFilePath = $($rootPath + "\secure-file\tools\secure-file.exe");
|
|
||||||
|
|
||||||
Invoke-Expression "& `"$secureFilePath`" -decrypt $($encKeystorePath) -secret $($env:keystore_dec_secret)"
|
|
||||||
Invoke-Expression "& `"$secureFilePath`" -decrypt $($encFdroidKeystorePath) -secret $($env:fdroid_apk_keystore_dec_secret)"
|
|
||||||
Invoke-Expression "& `"$secureFilePath`" -decrypt $($encUploadKeystorePath) -secret $($env:upload_keystore_dec_secret)"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Sign Google Play Bundle Release Configuration"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:upload_keystore_password)" `
|
|
||||||
"/p:AndroidSigningKeyStore=upload-keystore.jks" "/p:AndroidSigningStorePass=$($env:upload_keystore_password)" `
|
|
||||||
"/p:AndroidPackageFormat=aab" "/v:quiet"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Copy Google Play Bundle to project root"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$signedAabPath = $($rootPath + "\src\Android\bin\Release\com.x8bit.bitwarden-Signed.aab");
|
|
||||||
$signedAabDestPath = $($rootPath + "\com.x8bit.bitwarden.aab");
|
|
||||||
|
|
||||||
Copy-Item $signedAabPath $signedAabDestPath
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Sign APK Release Configuration"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:keystore_password)" `
|
|
||||||
"/p:AndroidSigningKeyStore=8bit.keystore" "/p:AndroidSigningStorePass=$($env:keystore_password)" "/v:quiet"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Copy Release APK to project root"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$signedApkPath = $($rootPath + "\src\Android\bin\Release\com.x8bit.bitwarden-Signed.apk");
|
|
||||||
$signedApkDestPath = $($rootPath + "\com.x8bit.bitwarden.apk");
|
|
||||||
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Clean Android and App"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
|
|
||||||
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Backup project files"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
Copy-Item $androidManifest $($androidManifest + ".original");
|
|
||||||
Copy-Item $androidPath $($androidPath + ".original");
|
|
||||||
Copy-Item $appPath $($appPath + ".original");
|
|
||||||
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Cleanup Android Manifest"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidManifest);
|
|
||||||
|
|
||||||
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
|
||||||
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
|
||||||
|
|
||||||
$firebaseReceiver1=$xml.SelectSingleNode(`
|
|
||||||
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdInternalReceiver']", `
|
|
||||||
$nsAndroid);
|
|
||||||
$firebaseReceiver1.ParentNode.RemoveChild($firebaseReceiver1);
|
|
||||||
|
|
||||||
$firebaseReceiver2=$xml.SelectSingleNode(`
|
|
||||||
"/manifest/application/receiver[@android:name='com.google.firebase.iid.FirebaseInstanceIdReceiver']", `
|
|
||||||
$nsAndroid);
|
|
||||||
$firebaseReceiver2.ParentNode.RemoveChild($firebaseReceiver2);
|
|
||||||
|
|
||||||
$xml.Save($androidManifest);
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Uninstall from Android.csproj"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidPath);
|
|
||||||
|
|
||||||
$ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
|
||||||
$ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
|
|
||||||
|
|
||||||
$firebaseNode=$xml.SelectSingleNode(`
|
|
||||||
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
|
||||||
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
|
||||||
|
|
||||||
$safetyNetNode=$xml.SelectSingleNode(`
|
|
||||||
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
|
||||||
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
|
||||||
|
|
||||||
$xml.Save($androidPath);
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Uninstall from App.csproj"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($appPath);
|
|
||||||
|
|
||||||
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
|
||||||
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
|
||||||
|
|
||||||
$xml.Save($appPath);
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Restore NuGet"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
Invoke-Expression "& nuget restore"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Build and Sign FDroid Configuration"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
|
|
||||||
"/p:Configuration=FDroid"
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:fdroid_apk_keystore_password)" `
|
|
||||||
"/p:AndroidSigningKeyStore=fdroid-keystore.jks" "/p:AndroidSigningStorePass=$($env:fdroid_apk_keystore_password)" `
|
|
||||||
"/v:quiet"
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Copy FDroid apk to project root"
|
|
||||||
echo "########################################"
|
|
||||||
|
|
||||||
$signedApkPath = $($rootPath + "\src\Android\bin\FDroid\com.x8bit.bitwarden-Signed.apk");
|
|
||||||
$signedApkDestPath = $($rootPath + "\com.x8bit.bitwarden-fdroid.apk");
|
|
||||||
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
|
||||||
|
|
||||||
echo "########################################"
|
|
||||||
echo "##### Done"
|
|
||||||
echo "########################################"
|
|
||||||
@@ -19,7 +19,7 @@ namespace Bit.App.Abstractions
|
|||||||
Task SelectFileAsync();
|
Task SelectFileAsync();
|
||||||
Task<string> DisplayPromptAync(string title = null, string description = null, string text = null,
|
Task<string> DisplayPromptAync(string title = null, string description = null, string text = null,
|
||||||
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false,
|
string okButtonText = null, string cancelButtonText = null, bool numericKeyboard = false,
|
||||||
bool autofocus = true);
|
bool autofocus = true, bool password = false);
|
||||||
void RateApp();
|
void RateApp();
|
||||||
bool SupportsFaceBiometric();
|
bool SupportsFaceBiometric();
|
||||||
Task<bool> SupportsFaceBiometricAsync();
|
Task<bool> SupportsFaceBiometricAsync();
|
||||||
@@ -44,5 +44,6 @@ namespace Bit.App.Abstractions
|
|||||||
void OpenAutofillSettings();
|
void OpenAutofillSettings();
|
||||||
bool UsingDarkTheme();
|
bool UsingDarkTheme();
|
||||||
long GetActiveTime();
|
long GetActiveTime();
|
||||||
|
void CloseMainApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/App/Abstractions/IPasswordRepromptService.cs
Normal file
11
src/App/Abstractions/IPasswordRepromptService.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface IPasswordRepromptService
|
||||||
|
{
|
||||||
|
string[] ProtectedFields { get; }
|
||||||
|
|
||||||
|
Task<bool> ShowPasswordPromptAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,11 +13,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="3.2.1" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.3.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.2" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2083" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ namespace Bit.App
|
|||||||
await SetMainPageAsync();
|
await SetMainPageAsync();
|
||||||
}
|
}
|
||||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||||
message.Command == "popAllAndGoToTabMyVault")
|
message.Command == "popAllAndGoToTabMyVault" ||
|
||||||
|
message.Command == "popAllAndGoToTabSend")
|
||||||
{
|
{
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
@@ -146,11 +147,15 @@ namespace Bit.App
|
|||||||
Options.MyVaultTile = false;
|
Options.MyVaultTile = false;
|
||||||
tabsPage.ResetToVaultPage();
|
tabsPage.ResetToVaultPage();
|
||||||
}
|
}
|
||||||
else
|
else if (message.Command == "popAllAndGoToTabGenerator")
|
||||||
{
|
{
|
||||||
Options.GeneratorTile = false;
|
Options.GeneratorTile = false;
|
||||||
tabsPage.ResetToGeneratorPage();
|
tabsPage.ResetToGeneratorPage();
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "popAllAndGoToTabSend")
|
||||||
|
{
|
||||||
|
tabsPage.ResetToSendPage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -173,6 +178,10 @@ namespace Bit.App
|
|||||||
SyncIfNeeded();
|
SyncIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
|
}
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +220,6 @@ namespace Bit.App
|
|||||||
private async void ResumedAsync()
|
private async void ResumedAsync()
|
||||||
{
|
{
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
_messagingService.Send("cancelVaultTimeoutTimer");
|
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
await ClearCacheIfNeededAsync();
|
await ClearCacheIfNeededAsync();
|
||||||
Prime();
|
Prime();
|
||||||
@@ -244,7 +252,8 @@ namespace Bit.App
|
|||||||
_collectionService.ClearAsync(userId),
|
_collectionService.ClearAsync(userId),
|
||||||
_passwordGenerationService.ClearAsync(),
|
_passwordGenerationService.ClearAsync(),
|
||||||
_vaultTimeoutService.ClearAsync(),
|
_vaultTimeoutService.ClearAsync(),
|
||||||
_stateService.PurgeAsync());
|
_stateService.PurgeAsync(),
|
||||||
|
_deviceActionService.ClearCacheAsync());
|
||||||
_vaultTimeoutService.BiometricLocked = true;
|
_vaultTimeoutService.BiometricLocked = true;
|
||||||
_searchService.ClearIndex();
|
_searchService.ClearIndex();
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
@@ -274,6 +283,10 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||||
}
|
}
|
||||||
|
else if (Options.CreateSend != null)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Current.MainPage = new TabsPage(Options);
|
Current.MainPage = new TabsPage(Options);
|
||||||
@@ -303,11 +316,7 @@ namespace Bit.App
|
|||||||
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||||
}
|
}
|
||||||
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
||||||
if (vaultTimeout > 0)
|
if (vaultTimeout == 0)
|
||||||
{
|
|
||||||
_messagingService.Send("scheduleVaultTimeoutTimer", vaultTimeout.Value);
|
|
||||||
}
|
|
||||||
else if (vaultTimeout == 0)
|
|
||||||
{
|
{
|
||||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
||||||
if (action == "logOut")
|
if (action == "logOut")
|
||||||
|
|||||||
@@ -1,114 +1,111 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
|
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Controls.CipherViewCell"
|
x:Class="Bit.App.Controls.CipherViewCell"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms">
|
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
|
||||||
|
StyleClass="list-row, list-row-platform"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0"
|
||||||
|
x:DataType="controls:CipherViewCellViewModel">
|
||||||
|
|
||||||
<Grid
|
<Grid.Resources>
|
||||||
x:Name="_grid"
|
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
||||||
StyleClass="list-row, list-row-platform"
|
<u:IconImageConverter x:Key="iconImageConverter"/>
|
||||||
RowSpacing="0"
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
ColumnSpacing="0"
|
</Grid.Resources>
|
||||||
x:DataType="controls:CipherViewCellViewModel">
|
|
||||||
|
|
||||||
<Grid.BindingContext>
|
<Grid.RowDefinitions>
|
||||||
<controls:CipherViewCellViewModel />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.BindingContext>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="40" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="60" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||||
|
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
|
<ff:CachedImage
|
||||||
|
Grid.Column="0"
|
||||||
|
BitmapOptimizations="True"
|
||||||
|
ErrorPlaceholder="login.png"
|
||||||
|
LoadingPlaceholder="login.png"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
WidthRequest="22"
|
||||||
|
HeightRequest="22"
|
||||||
|
IsVisible="{Binding ShowIconImage}"
|
||||||
|
Source="{Binding Cipher, Converter={StaticResource iconImageConverter}}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="40" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="60" />
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
StyleClass="list-title, list-title-platform"
|
||||||
|
Text="{Binding Cipher.Name}" />
|
||||||
|
<Label
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
|
Text="{Binding Cipher.SubTitle}" />
|
||||||
<controls:FaLabel
|
<controls:FaLabel
|
||||||
x:Name="_icon"
|
Grid.Column="1"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
HorizontalOptions="Start"
|
||||||
HorizontalOptions="Center"
|
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-icon, list-icon-platform"
|
StyleClass="list-title-icon"
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
<ff:CachedImage
|
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
|
||||||
x:Name="_image"
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="0"
|
|
||||||
BitmapOptimizations="True"
|
|
||||||
ErrorPlaceholder="login.png"
|
|
||||||
HorizontalOptions="Center"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
WidthRequest="22"
|
|
||||||
HeightRequest="22"
|
|
||||||
IsVisible="False"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
|
||||||
|
|
||||||
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Label
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="0"
|
|
||||||
StyleClass="list-title, list-title-platform"
|
|
||||||
Text="{Binding Cipher.Name, Mode=OneWay}" />
|
|
||||||
<Label
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
|
||||||
Text="{Binding Cipher.SubTitle, Mode=OneWay}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="1"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Cipher.Shared, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Shared}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="2"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Cipher.HasAttachments, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Attachments}" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<controls:MiButton
|
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="2"
|
|
||||||
Text=""
|
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
|
||||||
Clicked="MoreButton_Clicked"
|
|
||||||
VerticalOptions="CenterAndExpand"
|
|
||||||
HorizontalOptions="EndAndExpand"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Options}" />
|
AutomationProperties.Name="{u:I18n Shared}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="2"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Attachments}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</ViewCell>
|
<controls:MiButton
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text=""
|
||||||
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
|
Clicked="MoreButton_Clicked"
|
||||||
|
VerticalOptions="CenterAndExpand"
|
||||||
|
HorizontalOptions="EndAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
|
||||||
|
</controls:ExtendedGrid>
|
||||||
|
|||||||
@@ -1,45 +1,26 @@
|
|||||||
using Bit.App.Pages;
|
using System;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class CipherViewCell : ViewCell
|
public partial class CipherViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
public static readonly BindableProperty CipherProperty = BindableProperty.Create(
|
||||||
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
|
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
|
||||||
|
|
||||||
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
|
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
|
||||||
nameof(WebsiteIconsEnabled), typeof(bool), typeof(CipherViewCell), true, BindingMode.OneWay);
|
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell));
|
||||||
|
|
||||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||||
nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell));
|
nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell));
|
||||||
|
|
||||||
private readonly IEnvironmentService _environmentService;
|
|
||||||
|
|
||||||
private CipherViewCellViewModel _viewModel;
|
|
||||||
private bool _usingNativeCell;
|
|
||||||
|
|
||||||
public CipherViewCell()
|
public CipherViewCell()
|
||||||
{
|
{
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
InitializeComponent();
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
_viewModel = _grid.BindingContext as CipherViewCellViewModel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_usingNativeCell = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool WebsiteIconsEnabled
|
public bool? WebsiteIconsEnabled
|
||||||
{
|
{
|
||||||
get => (bool)GetValue(WebsiteIconsEnabledProperty);
|
get => (bool)GetValue(WebsiteIconsEnabledProperty);
|
||||||
set => SetValue(WebsiteIconsEnabledProperty, value);
|
set => SetValue(WebsiteIconsEnabledProperty, value);
|
||||||
@@ -60,130 +41,31 @@ namespace Bit.App.Controls
|
|||||||
protected override void OnPropertyChanged(string propertyName = null)
|
protected override void OnPropertyChanged(string propertyName = null)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(propertyName);
|
base.OnPropertyChanged(propertyName);
|
||||||
if (_usingNativeCell)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (propertyName == CipherProperty.PropertyName)
|
if (propertyName == CipherProperty.PropertyName)
|
||||||
{
|
{
|
||||||
_viewModel.Cipher = Cipher;
|
if (Cipher == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BindingContext = new CipherViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
|
||||||
}
|
}
|
||||||
}
|
else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
|
||||||
|
|
||||||
protected override void OnBindingContextChanged()
|
|
||||||
{
|
|
||||||
base.OnBindingContextChanged();
|
|
||||||
if (_usingNativeCell)
|
|
||||||
{
|
{
|
||||||
return;
|
if (Cipher == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
((CipherViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_image.Source = null;
|
|
||||||
CipherView cipher = null;
|
|
||||||
if (BindingContext is GroupingsPageListItem groupingsPageListItem)
|
|
||||||
{
|
|
||||||
cipher = groupingsPageListItem.Cipher;
|
|
||||||
}
|
|
||||||
else if (BindingContext is CipherView cv)
|
|
||||||
{
|
|
||||||
cipher = cv;
|
|
||||||
}
|
|
||||||
if (cipher != null)
|
|
||||||
{
|
|
||||||
var iconImage = GetIconImage(cipher);
|
|
||||||
if (iconImage.Item2 != null)
|
|
||||||
{
|
|
||||||
_image.IsVisible = true;
|
|
||||||
_icon.IsVisible = false;
|
|
||||||
_image.Source = iconImage.Item2;
|
|
||||||
_image.LoadingPlaceholder = "login.png";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_image.IsVisible = false;
|
|
||||||
_icon.IsVisible = true;
|
|
||||||
_icon.Text = iconImage.Item1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tuple<string, string> GetIconImage(CipherView cipher)
|
|
||||||
{
|
|
||||||
string icon = null;
|
|
||||||
string image = null;
|
|
||||||
switch (cipher.Type)
|
|
||||||
{
|
|
||||||
case CipherType.Login:
|
|
||||||
var loginIconImage = GetLoginIconImage(cipher);
|
|
||||||
icon = loginIconImage.Item1;
|
|
||||||
image = loginIconImage.Item2;
|
|
||||||
break;
|
|
||||||
case CipherType.SecureNote:
|
|
||||||
icon = "";
|
|
||||||
break;
|
|
||||||
case CipherType.Card:
|
|
||||||
icon = "";
|
|
||||||
break;
|
|
||||||
case CipherType.Identity:
|
|
||||||
icon = "";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return new Tuple<string, string>(icon, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tuple<string, string> GetLoginIconImage(CipherView cipher)
|
|
||||||
{
|
|
||||||
string icon = "";
|
|
||||||
string image = null;
|
|
||||||
if (cipher.Login.Uri != null)
|
|
||||||
{
|
|
||||||
var hostnameUri = cipher.Login.Uri;
|
|
||||||
var isWebsite = false;
|
|
||||||
|
|
||||||
if (hostnameUri.StartsWith(Constants.AndroidAppProtocol))
|
|
||||||
{
|
|
||||||
icon = "";
|
|
||||||
}
|
|
||||||
else if (hostnameUri.StartsWith(Constants.iOSAppProtocol))
|
|
||||||
{
|
|
||||||
icon = "";
|
|
||||||
}
|
|
||||||
else if (WebsiteIconsEnabled && !hostnameUri.Contains("://") && hostnameUri.Contains("."))
|
|
||||||
{
|
|
||||||
hostnameUri = string.Concat("http://", hostnameUri);
|
|
||||||
isWebsite = true;
|
|
||||||
}
|
|
||||||
else if (WebsiteIconsEnabled)
|
|
||||||
{
|
|
||||||
isWebsite = hostnameUri.StartsWith("http") && hostnameUri.Contains(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WebsiteIconsEnabled && isWebsite)
|
|
||||||
{
|
|
||||||
var hostname = CoreHelpers.GetHostname(hostnameUri);
|
|
||||||
var iconsUrl = _environmentService.IconsUrl;
|
|
||||||
if (string.IsNullOrWhiteSpace(iconsUrl))
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
|
|
||||||
{
|
|
||||||
iconsUrl = string.Format("{0}/icons", _environmentService.BaseUrl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iconsUrl = "https://icons.bitwarden.net";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
image = string.Format("{0}/{1}/icon.png", iconsUrl, hostname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Tuple<string, string>(icon, image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoreButton_Clicked(object sender, EventArgs e)
|
private void MoreButton_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ButtonCommand?.Execute(Cipher);
|
var cipher = ((sender as MiButton)?.BindingContext as CipherViewCellViewModel)?.Cipher;
|
||||||
|
if (cipher != null)
|
||||||
|
{
|
||||||
|
ButtonCommand?.Execute(cipher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,30 @@ namespace Bit.App.Controls
|
|||||||
public class CipherViewCellViewModel : ExtendedViewModel
|
public class CipherViewCellViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
|
private bool _websiteIconsEnabled;
|
||||||
|
|
||||||
|
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
|
||||||
|
{
|
||||||
|
Cipher = cipherView;
|
||||||
|
WebsiteIconsEnabled = websiteIconsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
public CipherView Cipher
|
public CipherView Cipher
|
||||||
{
|
{
|
||||||
get => _cipher;
|
get => _cipher;
|
||||||
set => SetProperty(ref _cipher, value);
|
set => SetProperty(ref _cipher, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool WebsiteIconsEnabled
|
||||||
|
{
|
||||||
|
get => _websiteIconsEnabled;
|
||||||
|
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowIconImage
|
||||||
|
{
|
||||||
|
get => WebsiteIconsEnabled && !string.IsNullOrWhiteSpace(Cipher.Login?.Uri) &&
|
||||||
|
Cipher.Login.Uri.StartsWith("http");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/App/Controls/ExtendedCollectionView.cs
Normal file
8
src/App/Controls/ExtendedCollectionView.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedCollectionView : CollectionView
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/App/Controls/ExtendedGrid.cs
Normal file
8
src/App/Controls/ExtendedGrid.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedGrid : Grid
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public class ExtendedListView : ListView
|
|
||||||
{
|
|
||||||
public ExtendedListView() { }
|
|
||||||
|
|
||||||
public ExtendedListView(ListViewCachingStrategy cachingStrategy)
|
|
||||||
: base(cachingStrategy) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
src/App/Controls/ExtendedStackLayout.cs
Normal file
8
src/App/Controls/ExtendedStackLayout.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedStackLayout : StackLayout
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,137 +1,132 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
|
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Controls.SendViewCell"
|
x:Class="Bit.App.Controls.SendViewCell"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities">
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
StyleClass="list-row, list-row-platform"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0"
|
||||||
|
x:DataType="controls:SendViewCellViewModel">
|
||||||
|
|
||||||
<Grid
|
<Grid.Resources>
|
||||||
x:Name="_grid"
|
<u:SendIconGlyphConverter x:Key="sendIconGlyphConverter"/>
|
||||||
StyleClass="list-row, list-row-platform"
|
</Grid.Resources>
|
||||||
RowSpacing="0"
|
|
||||||
ColumnSpacing="0"
|
|
||||||
x:DataType="controls:SendViewCellViewModel">
|
|
||||||
|
|
||||||
<Grid.BindingContext>
|
<Grid.RowDefinitions>
|
||||||
<controls:SendViewCellViewModel />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.BindingContext>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="40" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="60" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
Text="{Binding Send, Converter={StaticResource sendIconGlyphConverter}}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="40" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="60" />
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:FaLabel
|
<Label
|
||||||
x:Name="_icon"
|
LineBreakMode="TailTruncation"
|
||||||
Grid.Row="0"
|
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
HorizontalOptions="Center"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-icon, list-icon-platform"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
|
||||||
|
|
||||||
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Label
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="0"
|
|
||||||
StyleClass="list-title, list-title-platform"
|
|
||||||
Text="{Binding Send.Name, Mode=OneWay}" />
|
|
||||||
<Label
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="6"
|
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
|
||||||
Text="{Binding Send.DisplayDate, Mode=OneWay}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="1"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.Disabled, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Disabled}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="2"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.HasPassword, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Password}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="3"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="4"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.Expired, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Expired}" />
|
|
||||||
<controls:FaLabel
|
|
||||||
Grid.Column="5"
|
|
||||||
Grid.Row="0"
|
|
||||||
HorizontalOptions="Start"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-title-icon"
|
|
||||||
Margin="5, 0, 0, 0"
|
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.PendingDelete, Mode=OneWay}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n PendingDelete}" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<controls:MiButton
|
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
StyleClass="list-title, list-title-platform"
|
||||||
Text=""
|
Text="{Binding Send.Name}" />
|
||||||
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
<Label
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
LineBreakMode="TailTruncation"
|
||||||
Clicked="MoreButton_Clicked"
|
Grid.Column="0"
|
||||||
VerticalOptions="CenterAndExpand"
|
Grid.Row="1"
|
||||||
HorizontalOptions="EndAndExpand"
|
Grid.ColumnSpan="6"
|
||||||
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
|
Text="{Binding Send.DisplayDate}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Send.Disabled, Mode=OneTime}"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Options}" />
|
AutomationProperties.Name="{u:I18n Disabled}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="2"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Password}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="3"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="4"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Send.Expired, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Expired}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Grid.Column="5"
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-title-icon"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding Send.PendingDelete, Mode=OneTime}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n PendingDelete}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</ViewCell>
|
<controls:MiButton
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text=""
|
||||||
|
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||||
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
|
Clicked="MoreButton_Clicked"
|
||||||
|
VerticalOptions="CenterAndExpand"
|
||||||
|
HorizontalOptions="EndAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
|
||||||
|
</controls:ExtendedGrid>
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.App.Pages;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class SendViewCell : ViewCell
|
public partial class SendViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty SendProperty = BindableProperty.Create(
|
public static readonly BindableProperty SendProperty = BindableProperty.Create(
|
||||||
nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay);
|
nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay);
|
||||||
@@ -17,25 +13,11 @@ namespace Bit.App.Controls
|
|||||||
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
|
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
|
||||||
|
|
||||||
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
|
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
|
||||||
nameof(ShowOptions), typeof(bool), typeof(SendViewCell));
|
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
|
||||||
|
|
||||||
private readonly IEnvironmentService _environmentService;
|
|
||||||
|
|
||||||
private SendViewCellViewModel _viewModel;
|
|
||||||
private bool _usingNativeCell;
|
|
||||||
|
|
||||||
public SendViewCell()
|
public SendViewCell()
|
||||||
{
|
{
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
InitializeComponent();
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
_viewModel = _grid.BindingContext as SendViewCellViewModel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_usingNativeCell = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendView Send
|
public SendView Send
|
||||||
@@ -52,72 +34,30 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public bool ShowOptions
|
public bool ShowOptions
|
||||||
{
|
{
|
||||||
get => GetValue(ShowOptionsProperty) is bool && (bool)GetValue(ShowOptionsProperty);
|
get => (bool)GetValue(ShowOptionsProperty);
|
||||||
set => SetValue(ShowOptionsProperty, value);
|
set => SetValue(ShowOptionsProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(string propertyName = null)
|
protected override void OnPropertyChanged(string propertyName = null)
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(propertyName);
|
base.OnPropertyChanged(propertyName);
|
||||||
if (_usingNativeCell)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (propertyName == SendProperty.PropertyName)
|
if (propertyName == SendProperty.PropertyName)
|
||||||
{
|
{
|
||||||
_viewModel.Send = Send;
|
if (Send == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BindingContext = new SendViewCellViewModel(Send, ShowOptions);
|
||||||
}
|
}
|
||||||
else if (propertyName == ShowOptionsProperty.PropertyName)
|
|
||||||
{
|
|
||||||
_viewModel.ShowOptions = ShowOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBindingContextChanged()
|
|
||||||
{
|
|
||||||
base.OnBindingContextChanged();
|
|
||||||
if (_usingNativeCell)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendView send = null;
|
|
||||||
if (BindingContext is SendGroupingsPageListItem sendGroupingsPageListItem)
|
|
||||||
{
|
|
||||||
send = sendGroupingsPageListItem.Send;
|
|
||||||
}
|
|
||||||
else if (BindingContext is SendView sv)
|
|
||||||
{
|
|
||||||
send = sv;
|
|
||||||
}
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
var iconImage = GetIconImage(send);
|
|
||||||
_icon.IsVisible = true;
|
|
||||||
_icon.Text = iconImage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetIconImage(SendView send)
|
|
||||||
{
|
|
||||||
string icon = null;
|
|
||||||
switch (send.Type)
|
|
||||||
{
|
|
||||||
case SendType.Text:
|
|
||||||
icon = "\uf0f6"; // fa-file-text-o
|
|
||||||
break;
|
|
||||||
case SendType.File:
|
|
||||||
icon = "\uf016"; // fa-file-o
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoreButton_Clicked(object sender, EventArgs e)
|
private void MoreButton_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ButtonCommand?.Execute(Send);
|
var send = ((sender as MiButton)?.BindingContext as SendViewCellViewModel)?.Send;
|
||||||
|
if (send != null)
|
||||||
|
{
|
||||||
|
ButtonCommand?.Execute(send);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace Bit.App.Controls
|
|||||||
private SendView _send;
|
private SendView _send;
|
||||||
private bool _showOptions;
|
private bool _showOptions;
|
||||||
|
|
||||||
|
public SendViewCellViewModel(SendView sendView, bool showOptions)
|
||||||
|
{
|
||||||
|
Send = sendView;
|
||||||
|
ShowOptions = showOptions;
|
||||||
|
}
|
||||||
|
|
||||||
public SendView Send
|
public SendView Send
|
||||||
{
|
{
|
||||||
get => _send;
|
get => _send;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Enums;
|
using System;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.App.Models
|
namespace Bit.App.Models
|
||||||
{
|
{
|
||||||
@@ -19,6 +20,7 @@ namespace Bit.App.Models
|
|||||||
public string SaveCardExpYear { get; set; }
|
public string SaveCardExpYear { get; set; }
|
||||||
public string SaveCardCode { get; set; }
|
public string SaveCardCode { get; set; }
|
||||||
public bool IosExtension { get; set; }
|
public bool IosExtension { get; set; }
|
||||||
|
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
|
||||||
|
|
||||||
public void SetAllFrom(AppOptions o)
|
public void SetAllFrom(AppOptions o)
|
||||||
{
|
{
|
||||||
@@ -41,6 +43,7 @@ namespace Bit.App.Models
|
|||||||
SaveCardExpYear = o.SaveCardExpYear;
|
SaveCardExpYear = o.SaveCardExpYear;
|
||||||
SaveCardCode = o.SaveCardCode;
|
SaveCardCode = o.SaveCardCode;
|
||||||
IosExtension = o.IosExtension;
|
IosExtension = o.IosExtension;
|
||||||
|
CreateSend = o.CreateSend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Bit.Core.Models.Domain;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -37,7 +38,6 @@ namespace Bit.App.Pages
|
|||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
private int _invalidPinAttempts = 0;
|
|
||||||
private Tuple<bool, bool> _pinSet;
|
private Tuple<bool, bool> _pinSet;
|
||||||
|
|
||||||
public LockPageViewModel()
|
public LockPageViewModel()
|
||||||
@@ -203,11 +203,12 @@ namespace Bit.App.Pages
|
|||||||
_vaultTimeoutService.PinProtectedKey);
|
_vaultTimeoutService.PinProtectedKey);
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
failed = decPin != Pin;
|
failed = decPin != Pin;
|
||||||
if (!failed)
|
if (!failed)
|
||||||
{
|
{
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,6 +218,7 @@ namespace Bit.App.Pages
|
|||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
failed = false;
|
failed = false;
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +228,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
_invalidPinAttempts++;
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
if (_invalidPinAttempts >= 5)
|
if (invalidUnlockAttempts >= 5)
|
||||||
{
|
{
|
||||||
_messagingService.Send("logout");
|
_messagingService.Send("logout");
|
||||||
return;
|
return;
|
||||||
@@ -239,32 +241,31 @@ namespace Bit.App.Pages
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
||||||
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
var passwordValid = false;
|
var passwordValid = false;
|
||||||
if (keyHash != null)
|
|
||||||
|
if (storedKeyHash != null)
|
||||||
{
|
{
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
||||||
if (storedKeyHash != null)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||||
|
var request = new PasswordVerificationRequest();
|
||||||
|
request.MasterPasswordHash = keyHash;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
passwordValid = storedKeyHash == keyHash;
|
await _apiService.PostAccountVerifyPasswordAsync(request);
|
||||||
|
passwordValid = true;
|
||||||
|
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
|
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
||||||
}
|
}
|
||||||
else
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
||||||
var request = new PasswordVerificationRequest();
|
|
||||||
request.MasterPasswordHash = keyHash;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _apiService.PostAccountVerifyPasswordAsync(request);
|
|
||||||
passwordValid = true;
|
|
||||||
await _cryptoService.SetKeyHashAsync(keyHash);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
|
||||||
}
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
|
||||||
}
|
}
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
}
|
}
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
@@ -272,12 +273,13 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||||
}
|
}
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
@@ -288,6 +290,12 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
|
if (invalidUnlockAttempts >= 5)
|
||||||
|
{
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -125,6 +126,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||||
}
|
}
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
|
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
@@ -36,6 +35,10 @@
|
|||||||
ReturnCommand="{Binding LogInCommand}" />
|
ReturnCommand="{Binding LogInCommand}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<StackLayout Padding="10, 0">
|
||||||
|
<Button Text="{u:I18n LogIn}"
|
||||||
|
Clicked="LogIn_Clicked"></Button>
|
||||||
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
@@ -182,6 +183,7 @@ namespace Bit.App.Pages
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
|
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
if (RememberOrgIdentifier)
|
if (RememberOrgIdentifier)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
||||||
|
|||||||
@@ -138,9 +138,10 @@ namespace Bit.App.Pages
|
|||||||
var kdfIterations = 100000;
|
var kdfIterations = 100000;
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _userService.GetEmailAsync();
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||||
|
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
|
|
||||||
Tuple<SymmetricCryptoKey, CipherString> encKey;
|
Tuple<SymmetricCryptoKey, EncString> encKey;
|
||||||
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
||||||
if (existingEncKey == null)
|
if (existingEncKey == null)
|
||||||
{
|
{
|
||||||
@@ -174,7 +175,7 @@ namespace Bit.App.Pages
|
|||||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
||||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
await _cryptoService.SetKeyHashAsync(masterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|||||||
@@ -16,14 +16,21 @@
|
|||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Name="_cancelItem" />
|
x:Name="_cancelItem" />
|
||||||
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" Order="Primary"
|
|
||||||
x:Name="_continueItem" />
|
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<u:IsNullConverter x:Key="isNull" />
|
<u:IsNullConverter x:Key="isNull" />
|
||||||
|
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary"
|
||||||
|
x:Name="_moreItem" x:Key="moreItem"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
<ToolbarItem Text="{u:I18n UseAnotherTwoStepMethod}"
|
||||||
|
Clicked="Methods_Clicked"
|
||||||
|
Order="Secondary"
|
||||||
|
x:Name="_useAnotherTwoStepMethod"
|
||||||
|
x:Key="useAnotherTwoStepMethod" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
@@ -45,7 +52,8 @@
|
|||||||
Keyboard="Numeric"
|
Keyboard="Numeric"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}" />
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
|
TextChanged="Token_TextChanged"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
@@ -123,6 +131,12 @@
|
|||||||
Margin="10, 20, 10, 10"
|
Margin="10, 20, 10, 10"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<Button Text="{u:I18n Continue}"
|
||||||
|
IsEnabled="{Binding EnableContinue}"
|
||||||
|
IsVisible="{Binding ShowContinue}"
|
||||||
|
Clicked="Continue_Clicked"
|
||||||
|
Margin="10, 0"
|
||||||
|
x:Name="_continue"></Button>
|
||||||
<Button Text="{u:I18n SendVerificationCodeAgain}"
|
<Button Text="{u:I18n SendVerificationCodeAgain}"
|
||||||
IsVisible="{Binding EmailMethod}"
|
IsVisible="{Binding EmailMethod}"
|
||||||
Clicked="ResendEmail_Clicked"
|
Clicked="ResendEmail_Clicked"
|
||||||
@@ -131,9 +145,6 @@
|
|||||||
IsVisible="{Binding ShowTryAgain}"
|
IsVisible="{Binding ShowTryAgain}"
|
||||||
Clicked="TryAgain_Clicked"
|
Clicked="TryAgain_Clicked"
|
||||||
Margin="10, 0"></Button>
|
Margin="10, 0"></Button>
|
||||||
<Button Text="{u:I18n UseAnotherTwoStepMethod}"
|
|
||||||
Clicked="Methods_Clicked"
|
|
||||||
Margin="10, 0"></Button>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -45,26 +46,17 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
ToolbarItems.Remove(_cancelItem);
|
ToolbarItems.Remove(_cancelItem);
|
||||||
}
|
}
|
||||||
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_moreItem);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_useAnotherTwoStepMethod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HybridWebView DuoWebView { get; set; }
|
public HybridWebView DuoWebView { get; set; }
|
||||||
|
|
||||||
public void AddContinueButton()
|
|
||||||
{
|
|
||||||
if (!ToolbarItems.Contains(_continueItem))
|
|
||||||
{
|
|
||||||
ToolbarItems.Add(_continueItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveContinueButton()
|
|
||||||
{
|
|
||||||
if (ToolbarItems.Contains(_continueItem))
|
|
||||||
{
|
|
||||||
ToolbarItems.Remove(_continueItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -145,6 +137,21 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void More_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (!DoOnce())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel, null, AppResources.UseAnotherTwoStepMethod);
|
||||||
|
|
||||||
|
if (selection == AppResources.UseAnotherTwoStepMethod)
|
||||||
|
{
|
||||||
|
await _vm.AnotherMethodAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void ResendEmail_Clicked(object sender, EventArgs e)
|
private async void ResendEmail_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -195,5 +202,10 @@ namespace Bit.App.Pages
|
|||||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Token_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
_vm.EnableContinue = !string.IsNullOrWhiteSpace(e.NewTextValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace Bit.App.Pages
|
|||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
private string _webVaultUrl = "https://vault.bitwarden.com";
|
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||||
private bool _authingWithSso = false;
|
private bool _authingWithSso = false;
|
||||||
|
private bool _enableContinue = false;
|
||||||
|
private bool _showContinue = true;
|
||||||
|
|
||||||
public TwoFactorPageViewModel()
|
public TwoFactorPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -73,6 +75,18 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public bool ShowTryAgain => YubikeyMethod && Device.RuntimePlatform == Device.iOS;
|
public bool ShowTryAgain => YubikeyMethod && Device.RuntimePlatform == Device.iOS;
|
||||||
|
|
||||||
|
public bool ShowContinue
|
||||||
|
{
|
||||||
|
get => _showContinue;
|
||||||
|
set => SetProperty(ref _showContinue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableContinue
|
||||||
|
{
|
||||||
|
get => _enableContinue;
|
||||||
|
set => SetProperty(ref _enableContinue, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||||
AppResources.YubiKeyInstruction;
|
AppResources.YubiKeyInstruction;
|
||||||
|
|
||||||
@@ -169,14 +183,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
}
|
}
|
||||||
if (SelectedProviderType == null || DuoMethod)
|
ShowContinue = !(SelectedProviderType == null || DuoMethod);
|
||||||
{
|
|
||||||
page.RemoveContinueButton();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
page.AddContinueButton();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
@@ -245,7 +252,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");
|
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");
|
||||||
}
|
}
|
||||||
else if (method != AppResources.Cancel)
|
else if (method != AppResources.Cancel && method != null)
|
||||||
{
|
{
|
||||||
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
||||||
if (selected == SelectedProviderType)
|
if (selected == SelectedProviderType)
|
||||||
|
|||||||
@@ -43,57 +43,53 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<ListView x:Name="_listView"
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding History}"
|
ItemsSource="{Binding History}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
|
||||||
CachingStrategy="RecycleElement"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||||
<ViewCell>
|
<Grid
|
||||||
<Grid
|
StyleClass="list-row, list-row-platform"
|
||||||
StyleClass="list-row, list-row-platform"
|
Padding="10"
|
||||||
Padding="10"
|
RowSpacing="0"
|
||||||
RowSpacing="0"
|
ColumnSpacing="10">
|
||||||
ColumnSpacing="10">
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
StyleClass="list-title, list-title-platform"
|
StyleClass="list-title, list-title-platform"
|
||||||
TextType="Html"
|
TextType="Html"
|
||||||
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
||||||
<Label LineBreakMode="TailTruncation"
|
<Label LineBreakMode="TailTruncation"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" />
|
Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" />
|
||||||
<controls:FaButton
|
<controls:FaButton
|
||||||
StyleClass="list-row-button, list-row-button-platform"
|
StyleClass="list-row-button, list-row-button-platform"
|
||||||
Text=""
|
Text=""
|
||||||
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
|
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
|
||||||
CommandParameter="{Binding .}"
|
CommandParameter="{Binding .}"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n CopyPassword}" />
|
AutomationProperties.Name="{u:I18n CopyPassword}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -89,6 +89,18 @@
|
|||||||
StyleClass="text-muted, text-sm, text-bold"
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
</Frame>
|
</Frame>
|
||||||
|
<Frame
|
||||||
|
IsVisible="{Binding SendOptionsPolicyInEffect}"
|
||||||
|
Padding="10"
|
||||||
|
Margin="0, 12, 0, 0"
|
||||||
|
HasShadow="False"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
BorderColor="Accent">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n SendOptionsPolicyInEffect}"
|
||||||
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
|
HorizontalTextAlignment="Center" />
|
||||||
|
</Frame>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n Name}"
|
Text="{u:I18n Name}"
|
||||||
@@ -105,7 +117,7 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}">
|
IsVisible="{Binding ShowTypeButtons}">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n Type}"
|
Text="{u:I18n Type}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label" />
|
||||||
@@ -210,6 +222,7 @@
|
|||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n ChooseFile}"
|
Text="{u:I18n ChooseFile}"
|
||||||
|
IsVisible="{Binding IsAddFromShare, Converter={StaticResource inverseBool}}"
|
||||||
IsEnabled="{Binding SendEnabled}"
|
IsEnabled="{Binding SendEnabled}"
|
||||||
StyleClass="box-button-row"
|
StyleClass="box-button-row"
|
||||||
Clicked="ChooseFile_Clicked" />
|
Clicked="ChooseFile_Clicked" />
|
||||||
@@ -222,7 +235,7 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n TypeFileInfo}"
|
Text="{u:I18n TypeFileInfo}"
|
||||||
IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowTypeButtons}"
|
||||||
StyleClass="box-footer-label"
|
StyleClass="box-footer-label"
|
||||||
Margin="0,5,0,0" />
|
Margin="0,5,0,0" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
@@ -281,24 +294,23 @@
|
|||||||
x:Name="_btnOptions"
|
x:Name="_btnOptions"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{StaticResource PrimaryColor}"
|
TextColor="{StaticResource PrimaryColor}"
|
||||||
Margin="0"
|
Margin="0" />
|
||||||
Clicked="ToggleOptions_Clicked" />
|
|
||||||
<controls:FaButton
|
<controls:FaButton
|
||||||
x:Name="_btnOptionsUp"
|
x:Name="_btnOptionsUp"
|
||||||
Text=""
|
Text=""
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{StaticResource PrimaryColor}"
|
TextColor="{StaticResource PrimaryColor}"
|
||||||
Clicked="ToggleOptions_Clicked"
|
Clicked="ToggleOptions_Clicked"
|
||||||
IsVisible="{Binding ShowOptions}" />
|
IsVisible="False" />
|
||||||
<controls:FaButton
|
<controls:FaButton
|
||||||
x:Name="_btnOptionsDown"
|
x:Name="_btnOptionsDown"
|
||||||
Text=""
|
Text=""
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{StaticResource PrimaryColor}"
|
TextColor="{StaticResource PrimaryColor}"
|
||||||
Clicked="ToggleOptions_Clicked"
|
Clicked="ToggleOptions_Clicked"
|
||||||
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}" />
|
IsVisible="False" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout IsVisible="{Binding ShowOptions}">
|
<StackLayout IsVisible="True">
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
Margin="0,10,0,0">
|
Margin="0,10,0,0">
|
||||||
@@ -490,6 +502,20 @@
|
|||||||
StyleClass="box-footer-label"
|
StyleClass="box-footer-label"
|
||||||
Margin="0,5,0,0" />
|
Margin="0,5,0,0" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<StackLayout
|
||||||
|
StyleClass="box-row, box-row-switch"
|
||||||
|
Margin="0,5,0,0">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n HideEmail}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Send.HideEmail}"
|
||||||
|
IsEnabled="{Binding DisableHideEmailControl, Converter={StaticResource inverseBool}}"
|
||||||
|
HorizontalOptions="End"
|
||||||
|
Margin="10,0,0,0" />
|
||||||
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row, box-row-switch"
|
StyleClass="box-row, box-row-switch"
|
||||||
Margin="0,5,0,0">
|
Margin="0,5,0,0">
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -14,19 +17,24 @@ namespace Bit.App.Pages
|
|||||||
public partial class SendAddEditPage : BaseContentPage
|
public partial class SendAddEditPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
|
||||||
|
private AppOptions _appOptions;
|
||||||
private SendAddEditPageViewModel _vm;
|
private SendAddEditPageViewModel _vm;
|
||||||
|
|
||||||
public SendAddEditPage(
|
public SendAddEditPage(
|
||||||
|
AppOptions appOptions = null,
|
||||||
string sendId = null,
|
string sendId = null,
|
||||||
SendType? type = null)
|
SendType? type = null)
|
||||||
{
|
{
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as SendAddEditPageViewModel;
|
_vm = BindingContext as SendAddEditPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.SendId = sendId;
|
_vm.SendId = sendId;
|
||||||
_vm.Type = type;
|
_vm.Type = appOptions?.CreateSend?.Item1 ?? type;
|
||||||
SetActivityIndicator();
|
SetActivityIndicator();
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
@@ -74,6 +82,14 @@ namespace Bit.App.Pages
|
|||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
|
}
|
||||||
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
_broadcasterService.Subscribe(nameof(SendAddEditPage), message =>
|
_broadcasterService.Subscribe(nameof(SendAddEditPage), message =>
|
||||||
{
|
{
|
||||||
@@ -95,6 +111,7 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await HandleCreateRequest();
|
||||||
if (!_vm.EditMode && string.IsNullOrWhiteSpace(_vm.Send?.Name))
|
if (!_vm.EditMode && string.IsNullOrWhiteSpace(_vm.Send?.Name))
|
||||||
{
|
{
|
||||||
RequestFocus(_nameEntry);
|
RequestFocus(_nameEntry);
|
||||||
@@ -103,6 +120,15 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnBackButtonPressed()
|
||||||
|
{
|
||||||
|
if (_vm.IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_appOptions.CreateSend = null;
|
||||||
|
}
|
||||||
|
return base.OnBackButtonPressed();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
@@ -271,5 +297,33 @@ namespace Bit.App.Pages
|
|||||||
ToolbarItems.Remove(_shareLink);
|
ToolbarItems.Remove(_shareLink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task HandleCreateRequest()
|
||||||
|
{
|
||||||
|
if (_appOptions?.CreateSend == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm.IsAddFromShare = true;
|
||||||
|
|
||||||
|
var name = _appOptions.CreateSend.Item2;
|
||||||
|
_vm.Send.Name = name;
|
||||||
|
|
||||||
|
var type = _appOptions.CreateSend.Item1;
|
||||||
|
if (type == SendType.File)
|
||||||
|
{
|
||||||
|
_vm.FileData = _appOptions.CreateSend.Item3;
|
||||||
|
_vm.FileName = name;
|
||||||
|
FileType_Clicked(null, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var text = _appOptions.CreateSend.Item4;
|
||||||
|
_vm.Send.Text.Text = text;
|
||||||
|
TextType_Clicked(null, null);
|
||||||
|
}
|
||||||
|
_appOptions.CreateSend = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
private bool _sendEnabled;
|
private bool _sendEnabled;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
|
private bool _emailVerified;
|
||||||
private SendView _send;
|
private SendView _send;
|
||||||
private string _fileName;
|
private string _fileName;
|
||||||
private bool _showOptions;
|
private bool _showOptions;
|
||||||
@@ -42,6 +43,8 @@ namespace Bit.App.Pages
|
|||||||
nameof(IsText),
|
nameof(IsText),
|
||||||
nameof(IsFile),
|
nameof(IsFile),
|
||||||
};
|
};
|
||||||
|
private bool _disableHideEmail;
|
||||||
|
private bool _sendOptionsPolicyInEffect;
|
||||||
|
|
||||||
public SendAddEditPageViewModel()
|
public SendAddEditPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -91,6 +94,8 @@ namespace Bit.App.Pages
|
|||||||
public byte[] FileData { get; set; }
|
public byte[] FileData { get; set; }
|
||||||
public string NewPassword { get; set; }
|
public string NewPassword { get; set; }
|
||||||
public bool ShareOnSave { get; set; }
|
public bool ShareOnSave { get; set; }
|
||||||
|
public bool DisableHideEmailControl { get; set; }
|
||||||
|
public bool IsAddFromShare { get; set; }
|
||||||
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
||||||
@@ -194,6 +199,17 @@ namespace Bit.App.Pages
|
|||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public bool DisableHideEmail
|
||||||
|
{
|
||||||
|
get => _disableHideEmail;
|
||||||
|
set => SetProperty(ref _disableHideEmail, value);
|
||||||
|
}
|
||||||
|
public bool SendOptionsPolicyInEffect
|
||||||
|
{
|
||||||
|
get => _sendOptionsPolicyInEffect;
|
||||||
|
set => SetProperty(ref _sendOptionsPolicyInEffect, value);
|
||||||
|
}
|
||||||
|
public bool ShowTypeButtons => !EditMode && !IsAddFromShare;
|
||||||
public bool EditMode => !string.IsNullOrWhiteSpace(SendId);
|
public bool EditMode => !string.IsNullOrWhiteSpace(SendId);
|
||||||
public bool IsText => Send?.Type == SendType.Text;
|
public bool IsText => Send?.Type == SendType.Text;
|
||||||
public bool IsFile => Send?.Type == SendType.File;
|
public bool IsFile => Send?.Type == SendType.File;
|
||||||
@@ -205,7 +221,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||||
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||||
|
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
||||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||||
|
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||||
|
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> LoadAsync()
|
public async Task<bool> LoadAsync()
|
||||||
@@ -228,7 +247,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var defaultType = _canAccessPremium ? SendType.File : SendType.Text;
|
var defaultType = _canAccessPremium && _emailVerified ? SendType.File : SendType.Text;
|
||||||
Send = new SendView
|
Send = new SendView
|
||||||
{
|
{
|
||||||
Type = Type.GetValueOrDefault(defaultType),
|
Type = Type.GetValueOrDefault(defaultType),
|
||||||
@@ -243,6 +262,10 @@ namespace Bit.App.Pages
|
|||||||
_isOverridingPickers = false;
|
_isOverridingPickers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisableHideEmailControl = !SendEnabled ||
|
||||||
|
(!EditMode && DisableHideEmail) ||
|
||||||
|
(EditMode && DisableHideEmail && !Send.HideEmail);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +338,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (!_canAccessPremium)
|
if (!_canAccessPremium)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.PremiumRequired);
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_emailVerified)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!EditMode)
|
if (!EditMode)
|
||||||
@@ -354,10 +382,6 @@ namespace Bit.App.Pages
|
|||||||
var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData);
|
var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|
||||||
_platformUtilsService.ShowToast("success", null,
|
|
||||||
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
|
|
||||||
await Page.Navigation.PopModalAsync();
|
|
||||||
|
|
||||||
if (Device.RuntimePlatform == Device.Android && IsFile)
|
if (Device.RuntimePlatform == Device.Android && IsFile)
|
||||||
{
|
{
|
||||||
// Workaround for https://github.com/xamarin/Xamarin.Forms/issues/5418
|
// Workaround for https://github.com/xamarin/Xamarin.Forms/issues/5418
|
||||||
@@ -366,6 +390,21 @@ namespace Bit.App.Pages
|
|||||||
_messagingService.Send("sendUpdated");
|
_messagingService.Send("sendUpdated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ShareOnSave)
|
||||||
|
{
|
||||||
|
_platformUtilsService.ShowToast("success", null,
|
||||||
|
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_deviceActionService.CloseMainApp();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Page.Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
|
||||||
if (ShareOnSave)
|
if (ShareOnSave)
|
||||||
{
|
{
|
||||||
var savedSend = await _sendService.GetAsync(sendId);
|
var savedSend = await _sendService.GetAsync(sendId);
|
||||||
@@ -412,11 +451,37 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task TypeChangedAsync(SendType type)
|
public async Task TypeChangedAsync(SendType type)
|
||||||
{
|
{
|
||||||
|
if (!SendEnabled)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendDisabledWarning);
|
||||||
|
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_deviceActionService.CloseMainApp();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Page.Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Send != null)
|
if (Send != null)
|
||||||
{
|
{
|
||||||
if (!EditMode && type == SendType.File && !_canAccessPremium)
|
if (!EditMode && type == SendType.File && (!_canAccessPremium || !_emailVerified))
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.PremiumRequired);
|
if (!_canAccessPremium)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
|
||||||
|
}
|
||||||
|
else if (!_emailVerified)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_deviceActionService.CloseMainApp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
type = SendType.Text;
|
type = SendType.Text;
|
||||||
}
|
}
|
||||||
Send.Type = type;
|
Send.Type = type;
|
||||||
|
|||||||
@@ -44,34 +44,32 @@
|
|||||||
<controls:SendViewCell
|
<controls:SendViewCell
|
||||||
Send="{Binding Send}"
|
Send="{Binding Send}"
|
||||||
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
|
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
|
||||||
ShowOptions="{Binding ShowOptions}" />
|
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="sendGroupTemplate"
|
<DataTemplate x:Key="sendGroupTemplate"
|
||||||
x:DataType="pages:SendGroupingsPageListItem">
|
x:DataType="pages:SendGroupingsPageListItem">
|
||||||
<ViewCell>
|
<controls:ExtendedStackLayout Orientation="Horizontal"
|
||||||
<StackLayout Orientation="Horizontal"
|
StyleClass="list-row, list-row-platform">
|
||||||
StyleClass="list-row, list-row-platform">
|
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
HorizontalOptions="Start"
|
||||||
HorizontalOptions="Start"
|
VerticalOptions="Center"
|
||||||
VerticalOptions="Center"
|
StyleClass="list-icon, list-icon-platform">
|
||||||
StyleClass="list-icon, list-icon-platform">
|
<controls:FaLabel.Effects>
|
||||||
<controls:FaLabel.Effects>
|
<effects:FixedSizeEffect />
|
||||||
<effects:FixedSizeEffect />
|
</controls:FaLabel.Effects>
|
||||||
</controls:FaLabel.Effects>
|
</controls:FaLabel>
|
||||||
</controls:FaLabel>
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
LineBreakMode="TailTruncation"
|
||||||
LineBreakMode="TailTruncation"
|
HorizontalOptions="FillAndExpand"
|
||||||
HorizontalOptions="FillAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
StyleClass="list-title" />
|
||||||
StyleClass="list-title" />
|
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
||||||
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
HorizontalOptions="End"
|
||||||
HorizontalOptions="End"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalTextAlignment="End"
|
||||||
HorizontalTextAlignment="End"
|
StyleClass="list-sub" />
|
||||||
StyleClass="list-sub" />
|
</controls:ExtendedStackLayout>
|
||||||
</StackLayout>
|
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
||||||
@@ -103,32 +101,25 @@
|
|||||||
Text="{Binding NoDataText}"
|
Text="{Binding NoDataText}"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n AddAnItem}"
|
Text="{u:I18n AddASend}"
|
||||||
Clicked="AddButton_Clicked"
|
Clicked="AddButton_Clicked" />
|
||||||
IsVisible="{Binding ShowAddSendButton}" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<controls:ExtendedListView
|
<RefreshView
|
||||||
x:Name="_listView"
|
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding GroupedSends}"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
HasUnevenRows="True"
|
|
||||||
RowHeight="-1"
|
|
||||||
RefreshCommand="{Binding RefreshCommand}"
|
|
||||||
IsPullToRefreshEnabled="True"
|
|
||||||
IsRefreshing="{Binding Refreshing}"
|
IsRefreshing="{Binding Refreshing}"
|
||||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
Command="{Binding RefreshCommand}">
|
||||||
IsGroupingEnabled="True"
|
<controls:ExtendedCollectionView
|
||||||
ItemSelected="RowSelected"
|
ItemsSource="{Binding GroupedSends}"
|
||||||
StyleClass="list, list-platform">
|
VerticalOptions="FillAndExpand"
|
||||||
<x:Arguments>
|
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||||
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
IsGrouped="True"
|
||||||
</x:Arguments>
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="RowSelected"
|
||||||
|
StyleClass="list, list-platform">
|
||||||
|
|
||||||
<ListView.GroupHeaderTemplate>
|
<CollectionView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:SendGroupingsPageListGroup">
|
<DataTemplate x:DataType="pages:SendGroupingsPageListGroup">
|
||||||
<ViewCell>
|
|
||||||
<StackLayout
|
<StackLayout
|
||||||
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
@@ -146,10 +137,10 @@
|
|||||||
<BoxView
|
<BoxView
|
||||||
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ViewCell>
|
</DataTemplate>
|
||||||
</DataTemplate>
|
</CollectionView.GroupHeaderTemplate>
|
||||||
</ListView.GroupHeaderTemplate>
|
</controls:ExtendedCollectionView>
|
||||||
</controls:ExtendedListView>
|
</RefreshView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ namespace Bit.App.Pages
|
|||||||
private readonly SendGroupingsPageViewModel _vm;
|
private readonly SendGroupingsPageViewModel _vm;
|
||||||
private readonly string _pageName;
|
private readonly string _pageName;
|
||||||
|
|
||||||
private PreviousPageInfo _previousPage;
|
private AppOptions _appOptions;
|
||||||
|
|
||||||
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
|
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
|
||||||
PreviousPageInfo previousPage = null)
|
AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(SendGroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ListView = _listView;
|
SetActivityIndicator(_mainContent);
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
@@ -35,7 +35,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.MainPage = mainPage;
|
_vm.MainPage = mainPage;
|
||||||
_vm.Type = type;
|
_vm.Type = type;
|
||||||
_previousPage = previousPage;
|
_appOptions = appOptions;
|
||||||
if (pageTitle != null)
|
if (pageTitle != null)
|
||||||
{
|
{
|
||||||
_vm.PageTitle = pageTitle;
|
_vm.PageTitle = pageTitle;
|
||||||
@@ -44,7 +44,10 @@ namespace Bit.App.Pages
|
|||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
_absLayout.Children.Remove(_fab);
|
_absLayout.Children.Remove(_fab);
|
||||||
ToolbarItems.Add(_aboutIconItem);
|
if (type == null)
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_aboutIconItem);
|
||||||
|
}
|
||||||
ToolbarItems.Add(_addItem);
|
ToolbarItems.Add(_addItem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -55,8 +58,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedListView ListView { get; set; }
|
|
||||||
|
|
||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -109,8 +110,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ShowPreviousPageAsync();
|
|
||||||
AdjustToolbar();
|
AdjustToolbar();
|
||||||
|
await CheckAddRequest();
|
||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,14 +123,26 @@ namespace Bit.App.Pages
|
|||||||
_vm.DisableRefreshing();
|
_vm.DisableRefreshing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async Task CheckAddRequest()
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
if (_appOptions?.CreateSend != null)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
var page = new SendAddEditPage(_appOptions);
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.SelectedItem is SendGroupingsPageListItem item))
|
if (!(e.CurrentSelection?.FirstOrDefault() is SendGroupingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -172,28 +185,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
var page = new SendAddEditPage(null, _vm.Type);
|
var page = new SendAddEditPage(null, null, _vm.Type);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ShowPreviousPageAsync()
|
|
||||||
{
|
|
||||||
if (_previousPage == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_previousPage.Page == "view" && !string.IsNullOrWhiteSpace(_previousPage.SendId))
|
|
||||||
{
|
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new ViewPage(_previousPage.SendId)));
|
|
||||||
}
|
|
||||||
else if (_previousPage.Page == "edit" && !string.IsNullOrWhiteSpace(_previousPage.SendId))
|
|
||||||
{
|
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new AddEditPage(_previousPage.SendId)));
|
|
||||||
}
|
|
||||||
_previousPage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AdjustToolbar()
|
private void AdjustToolbar()
|
||||||
{
|
{
|
||||||
_addItem.IsEnabled = _vm.SendEnabled;
|
_addItem.IsEnabled = _vm.SendEnabled;
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ namespace Bit.App.Pages
|
|||||||
public SendGroupingsPageListGroup(string name, int count, bool doUpper = true, bool first = false)
|
public SendGroupingsPageListGroup(string name, int count, bool doUpper = true, bool first = false)
|
||||||
: this(new List<SendGroupingsPageListItem>(), name, count, doUpper, first) { }
|
: this(new List<SendGroupingsPageListItem>(), name, count, doUpper, first) { }
|
||||||
|
|
||||||
public SendGroupingsPageListGroup(List<SendGroupingsPageListItem> groupItems, string name, int count,
|
public SendGroupingsPageListGroup(List<SendGroupingsPageListItem> sendGroupItems, string name, int count,
|
||||||
bool doUpper = true, bool first = false)
|
bool doUpper = true, bool first = false)
|
||||||
{
|
{
|
||||||
AddRange(groupItems);
|
AddRange(sendGroupItems);
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
Name = "-";
|
Name = "-";
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ namespace Bit.App.Pages
|
|||||||
private bool _doingLoad;
|
private bool _doingLoad;
|
||||||
private bool _loading;
|
private bool _loading;
|
||||||
private bool _loaded;
|
private bool _loaded;
|
||||||
private bool _showAddSendButton;
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
private bool _showList;
|
private bool _showList;
|
||||||
private bool _syncRefreshing;
|
private bool _syncRefreshing;
|
||||||
@@ -91,11 +90,6 @@ namespace Bit.App.Pages
|
|||||||
get => _loaded;
|
get => _loaded;
|
||||||
set => SetProperty(ref _loaded, value);
|
set => SetProperty(ref _loaded, value);
|
||||||
}
|
}
|
||||||
public bool ShowAddSendButton
|
|
||||||
{
|
|
||||||
get => _showAddSendButton;
|
|
||||||
set => SetProperty(ref _showAddSendButton, value);
|
|
||||||
}
|
|
||||||
public bool ShowNoData
|
public bool ShowNoData
|
||||||
{
|
{
|
||||||
get => _showNoData;
|
get => _showNoData;
|
||||||
@@ -208,7 +202,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SelectSendAsync(SendView send)
|
public async Task SelectSendAsync(SendView send)
|
||||||
{
|
{
|
||||||
var page = new SendAddEditPage(send.Id);
|
var page = new SendAddEditPage(null, send.Id);
|
||||||
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,23 +60,22 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<ListView x:Name="_listView"
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding Sends}"
|
ItemsSource="{Binding Sends}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
SelectionMode="Single"
|
||||||
CachingStrategy="RecycleElement"
|
SelectionChanged="RowSelected"
|
||||||
ItemSelected="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:SendView">
|
<DataTemplate x:DataType="views:SendView">
|
||||||
<controls:SendViewCell
|
<controls:SendViewCell
|
||||||
Send="{Binding .}"
|
Send="{Binding .}"
|
||||||
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
|
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
|
||||||
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
|
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -87,15 +89,15 @@ namespace Bit.App.Pages
|
|||||||
Navigation.PopModalAsync(false);
|
Navigation.PopModalAsync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.SelectedItem is SendView send)
|
if (e.CurrentSelection?.FirstOrDefault() is SendView send)
|
||||||
{
|
{
|
||||||
await _vm.SelectSendAsync(send);
|
await _vm.SelectSendAsync(send);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SelectSendAsync(SendView send)
|
public async Task SelectSendAsync(SendView send)
|
||||||
{
|
{
|
||||||
var page = new SendAddEditPage(send.Id);
|
var page = new SendAddEditPage(null, send.Id);
|
||||||
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,11 +103,9 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyHash = await _cryptoService.HashPasswordAsync(_masterPassword, null);
|
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(_masterPassword, null);
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
|
if (!passwordValid)
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
|
||||||
if (storedKeyHash == null || keyHash == null || storedKeyHash != keyHash)
|
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
||||||
return;
|
return;
|
||||||
@@ -128,7 +126,7 @@ namespace Bit.App.Pages
|
|||||||
fileFormat = fileFormat == "encrypted_json" ? "json" : fileFormat;
|
fileFormat = fileFormat == "encrypted_json" ? "json" : fileFormat;
|
||||||
|
|
||||||
_defaultFilename = _exportService.GetFileName(null, fileFormat);
|
_defaultFilename = _exportService.GetFileName(null, fileFormat);
|
||||||
_exportResult = Encoding.ASCII.GetBytes(data);
|
_exportResult = Encoding.UTF8.GetBytes(data);
|
||||||
|
|
||||||
if (!_deviceActionService.SaveFile(_exportResult, null, _defaultFilename, null))
|
if (!_deviceActionService.SaveFile(_exportResult, null, _defaultFilename, null))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,27 +32,25 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<ListView x:Name="_listView"
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding Folders}"
|
ItemsSource="{Binding Folders}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
CachingStrategy="RecycleElement"
|
SelectionMode="Single"
|
||||||
ItemSelected="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:FolderView">
|
<DataTemplate x:DataType="views:FolderView">
|
||||||
<ViewCell>
|
<controls:ExtendedStackLayout
|
||||||
<StackLayout
|
StyleClass="list-row, list-row-platform"
|
||||||
StyleClass="list-row, list-row-platform"
|
Padding="10">
|
||||||
Padding="10">
|
<Label LineBreakMode="TailTruncation"
|
||||||
<Label LineBreakMode="TailTruncation"
|
StyleClass="list-title, list-title-platform"
|
||||||
StyleClass="list-title, list-title-platform"
|
Text="{Binding Name, Mode=OneWay}" />
|
||||||
Text="{Binding Name, Mode=OneWay}" />
|
</controls:ExtendedStackLayout>
|
||||||
</StackLayout>
|
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -32,14 +34,14 @@ namespace Bit.App.Pages
|
|||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.SelectedItem is FolderView folder))
|
if (!(e.CurrentSelection?.FirstOrDefault() is FolderView folder))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,23 +19,21 @@
|
|||||||
<DataTemplate
|
<DataTemplate
|
||||||
x:Key="regularTemplate"
|
x:Key="regularTemplate"
|
||||||
x:DataType="pages:SettingsPageListItem">
|
x:DataType="pages:SettingsPageListItem">
|
||||||
<ViewCell>
|
<controls:ExtendedStackLayout Orientation="Horizontal"
|
||||||
<StackLayout Orientation="Horizontal"
|
StyleClass="list-row, list-row-platform">
|
||||||
StyleClass="list-row, list-row-platform">
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
LineBreakMode="TailTruncation"
|
||||||
LineBreakMode="TailTruncation"
|
HorizontalOptions="StartAndExpand"
|
||||||
HorizontalOptions="StartAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
StyleClass="list-title"/>
|
||||||
StyleClass="list-title"/>
|
<Label Text="{Binding SubLabel, Mode=OneWay}"
|
||||||
<Label Text="{Binding SubLabel, Mode=OneWay}"
|
IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}"
|
||||||
IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}"
|
HorizontalOptions="End"
|
||||||
HorizontalOptions="End"
|
HorizontalTextAlignment="End"
|
||||||
HorizontalTextAlignment="End"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
TextColor="{Binding SubLabelColor}"
|
||||||
TextColor="{Binding SubLabelColor}"
|
StyleClass="list-sub" />
|
||||||
StyleClass="list-sub" />
|
</controls:ExtendedStackLayout>
|
||||||
</StackLayout>
|
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SettingsPageListItemSelector
|
<pages:SettingsPageListItemSelector
|
||||||
@@ -44,35 +42,32 @@
|
|||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ListView
|
<controls:ExtendedCollectionView
|
||||||
ItemsSource="{Binding GroupedItems}"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="True"
|
|
||||||
RowHeight="-1"
|
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
IsGroupingEnabled="True"
|
IsGrouped="True"
|
||||||
ItemSelected="RowSelected"
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
|
|
||||||
<ListView.GroupHeaderTemplate>
|
<CollectionView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:SettingsPageListGroup">
|
<DataTemplate x:DataType="pages:SettingsPageListGroup">
|
||||||
<ViewCell>
|
<StackLayout
|
||||||
<StackLayout
|
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
||||||
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
<BoxView
|
||||||
<BoxView
|
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
||||||
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
<Label
|
||||||
<Label
|
Text="{Binding Name}"
|
||||||
Text="{Binding Name}"
|
StyleClass="list-header, list-header-platform" />
|
||||||
StyleClass="list-header, list-header-platform" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ViewCell>
|
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
|
</StackLayout>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.GroupHeaderTemplate>
|
</CollectionView.GroupHeaderTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -41,14 +43,14 @@ namespace Bit.App.Pages
|
|||||||
return base.OnBackButtonPressed();
|
return base.OnBackButtonPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.SelectedItem is SettingsPageListItem item))
|
if (!(e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,17 +379,23 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
var accountItems = new List<SettingsPageListItem>
|
var accountItems = new List<SettingsPageListItem>
|
||||||
{
|
{
|
||||||
new SettingsPageListItem { Name = AppResources.ChangeMasterPassword },
|
|
||||||
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
||||||
new SettingsPageListItem { Name = AppResources.LogOut }
|
new SettingsPageListItem { Name = AppResources.LogOut }
|
||||||
};
|
};
|
||||||
|
if (IncludeLinksWithSubscriptionInfo())
|
||||||
|
{
|
||||||
|
accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword });
|
||||||
|
}
|
||||||
var toolsItems = new List<SettingsPageListItem>
|
var toolsItems = new List<SettingsPageListItem>
|
||||||
{
|
{
|
||||||
new SettingsPageListItem { Name = AppResources.ImportItems },
|
new SettingsPageListItem { Name = AppResources.ImportItems },
|
||||||
new SettingsPageListItem { Name = AppResources.ExportVault },
|
new SettingsPageListItem { Name = AppResources.ExportVault }
|
||||||
new SettingsPageListItem { Name = AppResources.ShareVault },
|
|
||||||
new SettingsPageListItem { Name = AppResources.WebVault }
|
|
||||||
};
|
};
|
||||||
|
if (IncludeLinksWithSubscriptionInfo())
|
||||||
|
{
|
||||||
|
toolsItems.Add(new SettingsPageListItem { Name = AppResources.ShareVault });
|
||||||
|
toolsItems.Add(new SettingsPageListItem { Name = AppResources.WebVault });
|
||||||
|
}
|
||||||
var otherItems = new List<SettingsPageListItem>
|
var otherItems = new List<SettingsPageListItem>
|
||||||
{
|
{
|
||||||
new SettingsPageListItem { Name = AppResources.Options },
|
new SettingsPageListItem { Name = AppResources.Options },
|
||||||
@@ -408,6 +414,15 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IncludeLinksWithSubscriptionInfo()
|
||||||
|
{
|
||||||
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private string GetVaultTimeoutActionFromKey(string key)
|
private string GetVaultTimeoutActionFromKey(string key)
|
||||||
{
|
{
|
||||||
return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
|
return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Bit.App.Pages
|
|||||||
};
|
};
|
||||||
Children.Add(_groupingsPage);
|
Children.Add(_groupingsPage);
|
||||||
|
|
||||||
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true))
|
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true, null, null, appOptions))
|
||||||
{
|
{
|
||||||
Title = AppResources.Send,
|
Title = AppResources.Send,
|
||||||
IconImageSource = "paper_plane.png",
|
IconImageSource = "paper_plane.png",
|
||||||
@@ -60,6 +60,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
appOptions.MyVaultTile = false;
|
appOptions.MyVaultTile = false;
|
||||||
}
|
}
|
||||||
|
else if (appOptions?.CreateSend != null)
|
||||||
|
{
|
||||||
|
ResetToSendPage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetToVaultPage()
|
public void ResetToVaultPage()
|
||||||
@@ -72,6 +76,11 @@ namespace Bit.App.Pages
|
|||||||
CurrentPage = _generatorPage;
|
CurrentPage = _generatorPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetToSendPage()
|
||||||
|
{
|
||||||
|
CurrentPage = _sendGroupingsPage;
|
||||||
|
}
|
||||||
|
|
||||||
protected async override void OnCurrentPageChanged()
|
protected async override void OnCurrentPageChanged()
|
||||||
{
|
{
|
||||||
if (CurrentPage is NavigationPage navPage)
|
if (CurrentPage is NavigationPage navPage)
|
||||||
|
|||||||
@@ -219,15 +219,40 @@
|
|||||||
Text="{Binding Cipher.Card.CardholderName}"
|
Text="{Binding Cipher.Card.CardholderName}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<Grid StyleClass="box-row, box-row-input">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n Number}"
|
Text="{u:I18n Number}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label"
|
||||||
<Entry
|
Grid.Row="0"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:MonoEntry
|
||||||
x:Name="_cardNumberEntry"
|
x:Name="_cardNumberEntry"
|
||||||
Text="{Binding Cipher.Card.Number}"
|
Text="{Binding Cipher.Card.Number}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value"
|
||||||
</StackLayout>
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Keyboard="Numeric"
|
||||||
|
IsPassword="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
IsTextPredictionEnabled="False" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowCardNumberIcon}"
|
||||||
|
Command="{Binding ToggleCardNumberCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row, box-row-input">
|
<StackLayout StyleClass="box-row, box-row-input">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n Brand}"
|
Text="{u:I18n Brand}"
|
||||||
@@ -530,6 +555,24 @@
|
|||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n PasswordPrompt}"
|
||||||
|
StyleClass="box-label-regular" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text=""
|
||||||
|
Command="{Binding PasswordPromptHelpCommand}"
|
||||||
|
TextColor="{StaticResource MutedColor}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding PasswordPrompt}"
|
||||||
|
Toggled="PasswordPrompt_Toggled"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
@@ -667,7 +710,10 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<controls:RepeaterView ItemsSource="{Binding Collections}" IsVisible="{Binding HasCollections}">
|
<controls:RepeaterView
|
||||||
|
x:Name="_collectionsRepeaterView"
|
||||||
|
ItemsSource="{Binding Collections}"
|
||||||
|
IsVisible="{Binding HasCollections}">
|
||||||
<controls:RepeaterView.ItemTemplate>
|
<controls:RepeaterView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="pages:CollectionViewModel">
|
<DataTemplate x:DataType="pages:CollectionViewModel">
|
||||||
<StackLayout Spacing="0" Padding="0">
|
<StackLayout Spacing="0" Padding="0">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
|
||||||
private AddEditPageViewModel _vm;
|
private AddEditPageViewModel _vm;
|
||||||
private bool _fromAutofill;
|
private bool _fromAutofill;
|
||||||
@@ -38,6 +39,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_fromAutofill = fromAutofill;
|
_fromAutofill = fromAutofill;
|
||||||
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
||||||
@@ -47,6 +49,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.CipherId = cipherId;
|
_vm.CipherId = cipherId;
|
||||||
_vm.FolderId = folderId == "none" ? null : folderId;
|
_vm.FolderId = folderId == "none" ? null : folderId;
|
||||||
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
|
_vm.CollectionIds = collectionId != null ? new HashSet<string>(new List<string> { collectionId }) : null;
|
||||||
|
_vm.CollectionsRepeaterView = _collectionsRepeaterView;
|
||||||
_vm.Type = type;
|
_vm.Type = type;
|
||||||
_vm.DefaultName = name ?? appOptions?.SaveName;
|
_vm.DefaultName = name ?? appOptions?.SaveName;
|
||||||
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
||||||
@@ -144,6 +147,14 @@ namespace Bit.App.Pages
|
|||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
|
}
|
||||||
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
await LoadOnAppearedAsync(_scrollView, true, async () =>
|
await LoadOnAppearedAsync(_scrollView, true, async () =>
|
||||||
{
|
{
|
||||||
var success = await _vm.LoadAsync(_appOptions);
|
var success = await _vm.LoadAsync(_appOptions);
|
||||||
@@ -158,6 +169,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
RequestFocus(_nameEntry);
|
RequestFocus(_nameEntry);
|
||||||
}
|
}
|
||||||
|
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,5 +388,10 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PasswordPrompt_Toggled(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
_vm.Cipher.Reprompt = e.Value ? CipherRepromptType.Password : CipherRepromptType.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -9,6 +10,7 @@ using Bit.Core.Utilities;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using View = Xamarin.Forms.View;
|
using View = Xamarin.Forms.View;
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ namespace Bit.App.Pages
|
|||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private bool _showNotesSeparator;
|
private bool _showNotesSeparator;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
|
private bool _showCardNumber;
|
||||||
private bool _showCardCode;
|
private bool _showCardCode;
|
||||||
private int _typeSelectedIndex;
|
private int _typeSelectedIndex;
|
||||||
private int _cardBrandSelectedIndex;
|
private int _cardBrandSelectedIndex;
|
||||||
@@ -38,6 +41,7 @@ namespace Bit.App.Pages
|
|||||||
private int _ownershipSelectedIndex;
|
private int _ownershipSelectedIndex;
|
||||||
private bool _hasCollections;
|
private bool _hasCollections;
|
||||||
private string _previousCipherId;
|
private string _previousCipherId;
|
||||||
|
private DateTime _lastHandledScrollTime;
|
||||||
private List<Core.Models.View.CollectionView> _writeableCollections;
|
private List<Core.Models.View.CollectionView> _writeableCollections;
|
||||||
private string[] _additionalCipherProperties = new string[]
|
private string[] _additionalCipherProperties = new string[]
|
||||||
{
|
{
|
||||||
@@ -82,10 +86,12 @@ namespace Bit.App.Pages
|
|||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
GeneratePasswordCommand = new Command(GeneratePassword);
|
GeneratePasswordCommand = new Command(GeneratePassword);
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||||
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
||||||
CheckPasswordCommand = new Command(CheckPasswordAsync);
|
CheckPasswordCommand = new Command(CheckPasswordAsync);
|
||||||
UriOptionsCommand = new Command<LoginUriView>(UriOptions);
|
UriOptionsCommand = new Command<LoginUriView>(UriOptions);
|
||||||
FieldOptionsCommand = new Command<AddEditPageFieldViewModel>(FieldOptions);
|
FieldOptionsCommand = new Command<AddEditPageFieldViewModel>(FieldOptions);
|
||||||
|
PasswordPromptHelpCommand = new Command(PasswordPromptHelp);
|
||||||
Uris = new ExtendedObservableCollection<LoginUriView>();
|
Uris = new ExtendedObservableCollection<LoginUriView>();
|
||||||
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
||||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||||
@@ -141,10 +147,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public Command GeneratePasswordCommand { get; set; }
|
public Command GeneratePasswordCommand { get; set; }
|
||||||
public Command TogglePasswordCommand { get; set; }
|
public Command TogglePasswordCommand { get; set; }
|
||||||
|
public Command ToggleCardNumberCommand { get; set; }
|
||||||
public Command ToggleCardCodeCommand { get; set; }
|
public Command ToggleCardCodeCommand { get; set; }
|
||||||
public Command CheckPasswordCommand { get; set; }
|
public Command CheckPasswordCommand { get; set; }
|
||||||
public Command UriOptionsCommand { get; set; }
|
public Command UriOptionsCommand { get; set; }
|
||||||
public Command FieldOptionsCommand { get; set; }
|
public Command FieldOptionsCommand { get; set; }
|
||||||
|
public Command PasswordPromptHelpCommand { get; set; }
|
||||||
public string CipherId { get; set; }
|
public string CipherId { get; set; }
|
||||||
public string OrganizationId { get; set; }
|
public string OrganizationId { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
@@ -161,6 +169,7 @@ namespace Bit.App.Pages
|
|||||||
public ExtendedObservableCollection<LoginUriView> Uris { get; set; }
|
public ExtendedObservableCollection<LoginUriView> Uris { get; set; }
|
||||||
public ExtendedObservableCollection<AddEditPageFieldViewModel> Fields { get; set; }
|
public ExtendedObservableCollection<AddEditPageFieldViewModel> Fields { get; set; }
|
||||||
public ExtendedObservableCollection<CollectionViewModel> Collections { get; set; }
|
public ExtendedObservableCollection<CollectionViewModel> Collections { get; set; }
|
||||||
|
public RepeaterView CollectionsRepeaterView { get; set; }
|
||||||
public int TypeSelectedIndex
|
public int TypeSelectedIndex
|
||||||
{
|
{
|
||||||
get => _typeSelectedIndex;
|
get => _typeSelectedIndex;
|
||||||
@@ -246,6 +255,15 @@ namespace Bit.App.Pages
|
|||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public bool ShowCardNumber
|
||||||
|
{
|
||||||
|
get => _showCardNumber;
|
||||||
|
set => SetProperty(ref _showCardNumber, value,
|
||||||
|
additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(ShowCardNumberIcon)
|
||||||
|
});
|
||||||
|
}
|
||||||
public bool ShowCardCode
|
public bool ShowCardCode
|
||||||
{
|
{
|
||||||
get => _showCardCode;
|
get => _showCardCode;
|
||||||
@@ -277,10 +295,12 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowUris => IsLogin && Cipher.Login.HasUris;
|
public bool ShowUris => IsLogin && Cipher.Login.HasUris;
|
||||||
public bool ShowAttachments => Cipher.HasAttachments;
|
public bool ShowAttachments => Cipher.HasAttachments;
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
|
public string ShowCardNumberIcon => ShowCardNumber ? "" : "";
|
||||||
public string ShowCardCodeIcon => ShowCardCode ? "" : "";
|
public string ShowCardCodeIcon => ShowCardCode ? "" : "";
|
||||||
public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4;
|
public int PasswordFieldColSpan => Cipher.ViewPassword ? 1 : 4;
|
||||||
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
public int TotpColumnSpan => Cipher.ViewPassword ? 1 : 2;
|
||||||
public bool AllowPersonal { get; set; }
|
public bool AllowPersonal { get; set; }
|
||||||
|
public bool PasswordPrompt => Cipher.Reprompt != CipherRepromptType.None;
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
@@ -691,6 +711,16 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleCardNumber()
|
||||||
|
{
|
||||||
|
ShowCardNumber = !ShowCardNumber;
|
||||||
|
if (EditMode && ShowCardNumber)
|
||||||
|
{
|
||||||
|
var task = _eventService.CollectAsync(
|
||||||
|
Core.Enums.EventType.Cipher_ClientToggledCardNumberVisible, CipherId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ToggleCardCode()
|
public void ToggleCardCode()
|
||||||
{
|
{
|
||||||
ShowCardCode = !ShowCardCode;
|
ShowCardCode = !ShowCardCode;
|
||||||
@@ -717,6 +747,11 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PasswordPromptHelp()
|
||||||
|
{
|
||||||
|
_platformUtilsService.LaunchUri("https://bitwarden.com/help/article/managing-items/#protect-individual-items");
|
||||||
|
}
|
||||||
|
|
||||||
private void TypeChanged()
|
private void TypeChanged()
|
||||||
{
|
{
|
||||||
if (Cipher != null && TypeSelectedIndex > -1)
|
if (Cipher != null && TypeSelectedIndex > -1)
|
||||||
@@ -769,13 +804,30 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
var cols = _writeableCollections.Where(c => c.OrganizationId == Cipher.OrganizationId)
|
var cols = _writeableCollections.Where(c => c.OrganizationId == Cipher.OrganizationId)
|
||||||
.Select(c => new CollectionViewModel { Collection = c }).ToList();
|
.Select(c => new CollectionViewModel { Collection = c }).ToList();
|
||||||
|
HasCollections = cols.Any();
|
||||||
Collections.ResetWithRange(cols);
|
Collections.ResetWithRange(cols);
|
||||||
|
Collections = new ExtendedObservableCollection<CollectionViewModel>(cols);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
HasCollections = false;
|
||||||
Collections.ResetWithRange(new List<CollectionViewModel>());
|
Collections.ResetWithRange(new List<CollectionViewModel>());
|
||||||
|
Collections = new ExtendedObservableCollection<CollectionViewModel>(new List<CollectionViewModel>());
|
||||||
}
|
}
|
||||||
HasCollections = Collections.Any();
|
}
|
||||||
|
|
||||||
|
public void HandleScroll()
|
||||||
|
{
|
||||||
|
// workaround for https://github.com/xamarin/Xamarin.Forms/issues/13607
|
||||||
|
// required for org ownership/collections to render properly in XF4.5+
|
||||||
|
if (!HasCollections ||
|
||||||
|
EditMode ||
|
||||||
|
(DateTime.Now - _lastHandledScrollTime < TimeSpan.FromMilliseconds(200)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CollectionsRepeaterView.ItemsSource = Collections;
|
||||||
|
_lastHandledScrollTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerCipherChanged()
|
private void TriggerCipherChanged()
|
||||||
|
|||||||
@@ -47,40 +47,35 @@
|
|||||||
Clicked="AddButton_Clicked"></Button>
|
Clicked="AddButton_Clicked"></Button>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<controls:ExtendedListView
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding GroupedItems}"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
IsGroupingEnabled="True"
|
IsGrouped="True"
|
||||||
ItemSelected="RowSelected"
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<x:Arguments>
|
|
||||||
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
|
||||||
</x:Arguments>
|
|
||||||
|
|
||||||
<ListView.GroupHeaderTemplate>
|
<CollectionView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
||||||
<ViewCell>
|
<StackLayout
|
||||||
<StackLayout
|
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
||||||
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
<BoxView
|
||||||
<BoxView
|
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
<Label
|
||||||
<Label
|
Text="{Binding Name}"
|
||||||
Text="{Binding Name}"
|
StyleClass="list-header, list-header-platform" />
|
||||||
StyleClass="list-header, list-header-platform" />
|
<Label
|
||||||
<Label
|
Text="{Binding ItemCount}"
|
||||||
Text="{Binding ItemCount}"
|
StyleClass="list-header-sub" />
|
||||||
StyleClass="list-header-sub" />
|
|
||||||
</StackLayout>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ViewCell>
|
</StackLayout>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.GroupHeaderTemplate>
|
</CollectionView.GroupHeaderTemplate>
|
||||||
</controls:ExtendedListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -13,6 +16,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
|
||||||
private AutofillCiphersPageViewModel _vm;
|
private AutofillCiphersPageViewModel _vm;
|
||||||
|
|
||||||
@@ -25,11 +29,20 @@ namespace Bit.App.Pages
|
|||||||
_vm.Init(appOptions);
|
_vm.Init(appOptions);
|
||||||
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
|
}
|
||||||
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -44,14 +57,23 @@ namespace Bit.App.Pages
|
|||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
protected override bool OnBackButtonPressed()
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_appOptions.Uri = null;
|
||||||
|
}
|
||||||
|
return base.OnBackButtonPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (e.SelectedItem is GroupingsPageListItem item && item.Cipher != null)
|
if (e.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item && item.Cipher != null)
|
||||||
{
|
{
|
||||||
await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill);
|
await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
|
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
private bool _showList;
|
private bool _showList;
|
||||||
@@ -35,6 +36,7 @@ namespace Bit.App.Pages
|
|||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
|
||||||
GroupedItems = new ExtendedObservableCollection<GroupingsPageListGroup>();
|
GroupedItems = new ExtendedObservableCollection<GroupingsPageListGroup>();
|
||||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||||
@@ -118,10 +120,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (_deviceActionService.SystemMajorVersion() < 21)
|
if (_deviceActionService.SystemMajorVersion() < 21)
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher);
|
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var autofillResponse = AppResources.Yes;
|
var autofillResponse = AppResources.Yes;
|
||||||
if (fuzzy)
|
if (fuzzy)
|
||||||
{
|
{
|
||||||
@@ -175,7 +181,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher);
|
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,15 +60,14 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<ListView x:Name="_listView"
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding Ciphers}"
|
ItemsSource="{Binding Ciphers}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
SelectionMode="Single"
|
||||||
CachingStrategy="RecycleElement"
|
SelectionChanged="RowSelected"
|
||||||
ItemSelected="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:CipherView">
|
<DataTemplate x:DataType="views:CipherView">
|
||||||
<controls:CipherViewCell
|
<controls:CipherViewCell
|
||||||
Cipher="{Binding .}"
|
Cipher="{Binding .}"
|
||||||
@@ -76,8 +75,8 @@
|
|||||||
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
|
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
|
||||||
/>
|
/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ using Bit.App.Resources;
|
|||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -119,15 +121,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.SelectedItem is CipherView cipher)
|
if (e.CurrentSelection?.FirstOrDefault() is CipherView cipher)
|
||||||
{
|
{
|
||||||
await _vm.SelectCipherAsync(cipher);
|
await _vm.SelectCipherAsync(cipher);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly ISearchService _searchService;
|
private readonly ISearchService _searchService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private CancellationTokenSource _searchCancellationTokenSource;
|
private CancellationTokenSource _searchCancellationTokenSource;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
@@ -35,6 +36,7 @@ namespace Bit.App.Pages
|
|||||||
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
|
||||||
Ciphers = new ExtendedObservableCollection<CipherView>();
|
Ciphers = new ExtendedObservableCollection<CipherView>();
|
||||||
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
|
||||||
@@ -182,7 +184,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (_deviceActionService.SystemMajorVersion() < 21)
|
if (_deviceActionService.SystemMajorVersion() < 21)
|
||||||
{
|
{
|
||||||
await Utilities.AppHelpers.CipherListOptions(Page, cipher);
|
await Utilities.AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -195,7 +197,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await Utilities.AppHelpers.CipherListOptions(Page, cipher);
|
await Utilities.AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Pages.GroupingsPage"
|
x:Class="Bit.App.Pages.GroupingsPage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
x:DataType="pages:GroupingsPageViewModel"
|
x:DataType="pages:GroupingsPageViewModel"
|
||||||
Title="{Binding PageTitle}"
|
Title="{Binding PageTitle}"
|
||||||
x:Name="_page">
|
x:Name="_page">
|
||||||
|
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:GroupingsPageViewModel />
|
<pages:GroupingsPageViewModel />
|
||||||
@@ -45,29 +45,27 @@
|
|||||||
|
|
||||||
<DataTemplate x:Key="groupTemplate"
|
<DataTemplate x:Key="groupTemplate"
|
||||||
x:DataType="pages:GroupingsPageListItem">
|
x:DataType="pages:GroupingsPageListItem">
|
||||||
<ViewCell>
|
<controls:ExtendedStackLayout Orientation="Horizontal"
|
||||||
<StackLayout Orientation="Horizontal"
|
StyleClass="list-row, list-row-platform">
|
||||||
StyleClass="list-row, list-row-platform">
|
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
HorizontalOptions="Start"
|
||||||
HorizontalOptions="Start"
|
VerticalOptions="Center"
|
||||||
VerticalOptions="Center"
|
StyleClass="list-icon, list-icon-platform">
|
||||||
StyleClass="list-icon, list-icon-platform">
|
<controls:FaLabel.Effects>
|
||||||
<controls:FaLabel.Effects>
|
<effects:FixedSizeEffect />
|
||||||
<effects:FixedSizeEffect />
|
</controls:FaLabel.Effects>
|
||||||
</controls:FaLabel.Effects>
|
</controls:FaLabel>
|
||||||
</controls:FaLabel>
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
LineBreakMode="TailTruncation"
|
||||||
LineBreakMode="TailTruncation"
|
HorizontalOptions="FillAndExpand"
|
||||||
HorizontalOptions="FillAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
StyleClass="list-title"/>
|
||||||
StyleClass="list-title"/>
|
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
||||||
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
HorizontalOptions="End"
|
||||||
HorizontalOptions="End"
|
VerticalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalTextAlignment="End"
|
||||||
HorizontalTextAlignment="End"
|
StyleClass="list-sub"/>
|
||||||
StyleClass="list-sub"/>
|
</controls:ExtendedStackLayout>
|
||||||
</StackLayout>
|
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
|
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
|
||||||
@@ -89,27 +87,21 @@
|
|||||||
IsVisible="{Binding ShowAddCipherButton}"></Button>
|
IsVisible="{Binding ShowAddCipherButton}"></Button>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<controls:ExtendedListView
|
<RefreshView
|
||||||
x:Name="_listView"
|
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding GroupedItems}"
|
|
||||||
VerticalOptions="FillAndExpand"
|
|
||||||
HasUnevenRows="True"
|
|
||||||
RowHeight="-1"
|
|
||||||
RefreshCommand="{Binding RefreshCommand}"
|
|
||||||
IsPullToRefreshEnabled="True"
|
|
||||||
IsRefreshing="{Binding Refreshing}"
|
IsRefreshing="{Binding Refreshing}"
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
Command="{Binding RefreshCommand}">
|
||||||
IsGroupingEnabled="True"
|
<controls:ExtendedCollectionView
|
||||||
ItemSelected="RowSelected"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
StyleClass="list, list-platform">
|
VerticalOptions="FillAndExpand"
|
||||||
<x:Arguments>
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
IsGrouped="True"
|
||||||
</x:Arguments>
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="RowSelected"
|
||||||
|
StyleClass="list, list-platform">
|
||||||
|
|
||||||
<ListView.GroupHeaderTemplate>
|
<CollectionView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
||||||
<ViewCell>
|
|
||||||
<StackLayout
|
<StackLayout
|
||||||
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
@@ -126,10 +118,10 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ViewCell>
|
</DataTemplate>
|
||||||
</DataTemplate>
|
</CollectionView.GroupHeaderTemplate>
|
||||||
</ListView.GroupHeaderTemplate>
|
</controls:ExtendedCollectionView>
|
||||||
</controls:ExtendedListView>
|
</RefreshView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
@@ -9,6 +8,7 @@ using Bit.Core.Utilities;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -33,7 +33,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ListView = _listView;
|
|
||||||
SetActivityIndicator(_mainContent);
|
SetActivityIndicator(_mainContent);
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
@@ -73,8 +72,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedListView ListView { get; set; }
|
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -197,14 +194,14 @@ namespace Bit.App.Pages
|
|||||||
_vm.DisableRefreshing();
|
_vm.DisableRefreshing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ListView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.SelectedItem is GroupingsPageListItem item))
|
if (!(e.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,30 +75,30 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else if (Folder != null)
|
else if (Folder != null)
|
||||||
{
|
{
|
||||||
_icon = Folder.Id == null ? "" : "";
|
_icon = Folder.Id == null ? "\uf115" : "\uf07c"; // fa-folder-open-o : fa-folder-open
|
||||||
}
|
}
|
||||||
else if (Collection != null)
|
else if (Collection != null)
|
||||||
{
|
{
|
||||||
_icon = "";
|
_icon = "\uf1b2"; // fa-cube
|
||||||
}
|
}
|
||||||
else if (Type != null)
|
else if (Type != null)
|
||||||
{
|
{
|
||||||
switch (Type.Value)
|
switch (Type.Value)
|
||||||
{
|
{
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
_icon = "";
|
_icon = "\uf0ac"; // fa-globe
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
_icon = "";
|
_icon = "\uf24a"; // fa-sticky-note-o
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
_icon = "";
|
_icon = "\uf09d"; // fa-credit-card
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
_icon = "";
|
_icon = "\uf2c3"; // fa-id-card-o
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_icon = "";
|
_icon = "\uf0ac"; // fa-globe
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
|
|
||||||
public GroupingsPageViewModel()
|
public GroupingsPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -60,6 +61,7 @@ namespace Bit.App.Pages
|
|||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
PageTitle = AppResources.MyVault;
|
PageTitle = AppResources.MyVault;
|
||||||
@@ -514,7 +516,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher);
|
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,57 +34,53 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<ListView x:Name="_listView"
|
<controls:ExtendedCollectionView
|
||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding History}"
|
ItemsSource="{Binding History}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HasUnevenRows="true"
|
|
||||||
CachingStrategy="RecycleElement"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<ListView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:PasswordHistoryView">
|
<DataTemplate x:DataType="views:PasswordHistoryView">
|
||||||
<ViewCell>
|
<Grid
|
||||||
<Grid
|
StyleClass="list-row, list-row-platform"
|
||||||
StyleClass="list-row, list-row-platform"
|
Padding="10"
|
||||||
Padding="10"
|
RowSpacing="0"
|
||||||
RowSpacing="0"
|
ColumnSpacing="10">
|
||||||
ColumnSpacing="10">
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
StyleClass="list-title, list-title-platform"
|
StyleClass="list-title, list-title-platform"
|
||||||
TextType="Html"
|
TextType="Html"
|
||||||
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
||||||
<Label LineBreakMode="TailTruncation"
|
<Label LineBreakMode="TailTruncation"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
Text="{Binding LastUsedDate, Mode=OneWay, Converter={StaticResource dateTime}}" />
|
Text="{Binding LastUsedDate, Mode=OneWay, Converter={StaticResource dateTime}}" />
|
||||||
<controls:FaButton
|
<controls:FaButton
|
||||||
StyleClass="list-row-button, list-row-button-platform"
|
StyleClass="list-row-button, list-row-button-platform"
|
||||||
Text=""
|
Text=""
|
||||||
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
|
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
|
||||||
CommandParameter="{Binding .}"
|
CommandParameter="{Binding .}"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n CopyPassword}" />
|
AutomationProperties.Name="{u:I18n CopyPassword}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ViewCell>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListView.ItemTemplate>
|
</CollectionView.ItemTemplate>
|
||||||
</ListView>
|
</controls:ExtendedCollectionView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -224,24 +224,41 @@
|
|||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n Number}"
|
Text="{u:I18n Number}"
|
||||||
StyleClass="box-label"
|
StyleClass="box-label"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0" />
|
Grid.Column="0" />
|
||||||
<Label
|
<controls:MonoLabel
|
||||||
|
Text="{Binding Cipher.Card.MaskedNumber, Mode=OneWay}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
IsVisible="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}" />
|
||||||
|
<controls:MonoLabel
|
||||||
Text="{Binding Cipher.Card.Number, Mode=OneWay}"
|
Text="{Binding Cipher.Card.Number, Mode=OneWay}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0" />
|
Grid.Column="0"
|
||||||
|
IsVisible="{Binding ShowCardNumber}" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowCardNumberIcon}"
|
||||||
|
Command="{Binding ToggleCardNumberCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
<controls:FaButton
|
<controls:FaButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text=""
|
Text=""
|
||||||
Command="{Binding CopyCommand}"
|
Command="{Binding CopyCommand}"
|
||||||
CommandParameter="CardNumber"
|
CommandParameter="CardNumber"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="2"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n CopyNumber}" />
|
AutomationProperties.Name="{u:I18n CopyNumber}" />
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new AddEditPage(_vm.CipherId)));
|
await Navigation.PushModalAsync(new NavigationPage(new AddEditPage(_vm.CipherId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,6 +146,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var page = new AttachmentsPage(_vm.CipherId);
|
var page = new AttachmentsPage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
@@ -151,6 +159,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var page = new SharePage(_vm.CipherId);
|
var page = new SharePage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
@@ -160,6 +172,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (await _vm.DeleteAsync())
|
if (await _vm.DeleteAsync())
|
||||||
{
|
{
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
@@ -171,6 +187,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var page = new CollectionsPage(_vm.CipherId);
|
var page = new CollectionsPage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
@@ -180,6 +200,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
var page = new AddEditPage(_vm.CipherId, cloneMode: true, viewPage: this);
|
var page = new AddEditPage(_vm.CipherId, cloneMode: true, viewPage: this);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
@@ -205,6 +229,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
||||||
AppResources.Delete, options.ToArray());
|
AppResources.Delete, options.ToArray());
|
||||||
|
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (selection == AppResources.Delete)
|
if (selection == AppResources.Delete)
|
||||||
{
|
{
|
||||||
if (await _vm.DeleteAsync())
|
if (await _vm.DeleteAsync())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -23,10 +24,12 @@ namespace Bit.App.Pages
|
|||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private List<ViewPageFieldViewModel> _fields;
|
private List<ViewPageFieldViewModel> _fields;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
|
private bool _showCardNumber;
|
||||||
private bool _showCardCode;
|
private bool _showCardCode;
|
||||||
private string _totpCode;
|
private string _totpCode;
|
||||||
private string _totpCodeFormatted;
|
private string _totpCodeFormatted;
|
||||||
@@ -36,6 +39,7 @@ namespace Bit.App.Pages
|
|||||||
private string _previousCipherId;
|
private string _previousCipherId;
|
||||||
private byte[] _attachmentData;
|
private byte[] _attachmentData;
|
||||||
private string _attachmentFilename;
|
private string _attachmentFilename;
|
||||||
|
private bool _passwordReprompted;
|
||||||
|
|
||||||
public ViewPageViewModel()
|
public ViewPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -47,11 +51,13 @@ namespace Bit.App.Pages
|
|||||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
||||||
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
||||||
CopyFieldCommand = new Command<FieldView>(CopyField);
|
CopyFieldCommand = new Command<FieldView>(CopyField);
|
||||||
LaunchUriCommand = new Command<LoginUriView>(LaunchUri);
|
LaunchUriCommand = new Command<LoginUriView>(LaunchUri);
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||||
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
||||||
CheckPasswordCommand = new Command(CheckPasswordAsync);
|
CheckPasswordCommand = new Command(CheckPasswordAsync);
|
||||||
DownloadAttachmentCommand = new Command<AttachmentView>(DownloadAttachmentAsync);
|
DownloadAttachmentCommand = new Command<AttachmentView>(DownloadAttachmentAsync);
|
||||||
@@ -64,6 +70,7 @@ namespace Bit.App.Pages
|
|||||||
public Command CopyFieldCommand { get; set; }
|
public Command CopyFieldCommand { get; set; }
|
||||||
public Command LaunchUriCommand { get; set; }
|
public Command LaunchUriCommand { get; set; }
|
||||||
public Command TogglePasswordCommand { get; set; }
|
public Command TogglePasswordCommand { get; set; }
|
||||||
|
public Command ToggleCardNumberCommand { get; set; }
|
||||||
public Command ToggleCardCodeCommand { get; set; }
|
public Command ToggleCardCodeCommand { get; set; }
|
||||||
public Command CheckPasswordCommand { get; set; }
|
public Command CheckPasswordCommand { get; set; }
|
||||||
public Command DownloadAttachmentCommand { get; set; }
|
public Command DownloadAttachmentCommand { get; set; }
|
||||||
@@ -109,6 +116,15 @@ namespace Bit.App.Pages
|
|||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public bool ShowCardNumber
|
||||||
|
{
|
||||||
|
get => _showCardNumber;
|
||||||
|
set => SetProperty(ref _showCardNumber, value,
|
||||||
|
additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(ShowCardNumberIcon)
|
||||||
|
});
|
||||||
|
}
|
||||||
public bool ShowCardCode
|
public bool ShowCardCode
|
||||||
{
|
{
|
||||||
get => _showCardCode;
|
get => _showCardCode;
|
||||||
@@ -188,6 +204,7 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowTotp => IsLogin && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
public bool ShowTotp => IsLogin && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||||
!string.IsNullOrWhiteSpace(TotpCodeFormatted);
|
!string.IsNullOrWhiteSpace(TotpCodeFormatted);
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
|
public string ShowCardNumberIcon => ShowCardNumber ? "" : "";
|
||||||
public string ShowCardCodeIcon => ShowCardCode ? "" : "";
|
public string ShowCardCodeIcon => ShowCardCode ? "" : "";
|
||||||
public string TotpCodeFormatted
|
public string TotpCodeFormatted
|
||||||
{
|
{
|
||||||
@@ -226,7 +243,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
Cipher = await cipher.DecryptAsync();
|
Cipher = await cipher.DecryptAsync();
|
||||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
||||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(Cipher, f)).ToList();
|
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||||
|
|
||||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||||
(Cipher.OrganizationUseTotp || CanAccessPremium))
|
(Cipher.OrganizationUseTotp || CanAccessPremium))
|
||||||
@@ -259,8 +276,13 @@ namespace Bit.App.Pages
|
|||||||
_totpInterval = null;
|
_totpInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TogglePassword()
|
public async void TogglePassword()
|
||||||
{
|
{
|
||||||
|
if (! await PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
if (ShowPassword)
|
if (ShowPassword)
|
||||||
{
|
{
|
||||||
@@ -268,8 +290,26 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleCardCode()
|
public async void ToggleCardNumber()
|
||||||
{
|
{
|
||||||
|
if (!await PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShowCardNumber = !ShowCardNumber;
|
||||||
|
if (ShowCardNumber)
|
||||||
|
{
|
||||||
|
var task = _eventService.CollectAsync(
|
||||||
|
Core.Enums.EventType.Cipher_ClientToggledCardNumberVisible, CipherId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ToggleCardCode()
|
||||||
|
{
|
||||||
|
if (!await PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
ShowCardCode = !ShowCardCode;
|
ShowCardCode = !ShowCardCode;
|
||||||
if (ShowCardCode)
|
if (ShowCardCode)
|
||||||
{
|
{
|
||||||
@@ -474,10 +514,15 @@ namespace Bit.App.Pages
|
|||||||
canOpenFile = false;
|
canOpenFile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = await _cipherService.DownloadAndDecryptAttachmentAsync(attachment, Cipher.OrganizationId);
|
var data = await _cipherService.DownloadAndDecryptAttachmentAsync(Cipher.Id, attachment, Cipher.OrganizationId);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
@@ -564,6 +609,11 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void CopyAsync(string id, string text = null)
|
private async void CopyAsync(string id, string text = null)
|
||||||
{
|
{
|
||||||
|
if (_passwordRepromptService.ProtectedFields.Contains(id) && !await PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string name = null;
|
string name = null;
|
||||||
if (id == "LoginUsername")
|
if (id == "LoginUsername")
|
||||||
{
|
{
|
||||||
@@ -638,16 +688,28 @@ namespace Bit.App.Pages
|
|||||||
_platformUtilsService.LaunchUri(uri.LaunchUri);
|
_platformUtilsService.LaunchUri(uri.LaunchUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal async Task<bool> PromptPasswordAsync()
|
||||||
|
{
|
||||||
|
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ViewPageFieldViewModel : ExtendedViewModel
|
public class ViewPageFieldViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
|
private ViewPageViewModel _vm;
|
||||||
private FieldView _field;
|
private FieldView _field;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private bool _showHiddenValue;
|
private bool _showHiddenValue;
|
||||||
|
|
||||||
public ViewPageFieldViewModel(CipherView cipher, FieldView field)
|
public ViewPageFieldViewModel(ViewPageViewModel vm, CipherView cipher, FieldView field)
|
||||||
{
|
{
|
||||||
|
_vm = vm;
|
||||||
_cipher = cipher;
|
_cipher = cipher;
|
||||||
Field = field;
|
Field = field;
|
||||||
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
||||||
@@ -688,8 +750,12 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
|
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
|
||||||
!string.IsNullOrWhiteSpace(_field.Value) && !(IsHiddenType && !_cipher.ViewPassword);
|
!string.IsNullOrWhiteSpace(_field.Value) && !(IsHiddenType && !_cipher.ViewPassword);
|
||||||
|
|
||||||
public void ToggleHiddenValue()
|
public async void ToggleHiddenValue()
|
||||||
{
|
{
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
ShowHiddenValue = !ShowHiddenValue;
|
ShowHiddenValue = !ShowHiddenValue;
|
||||||
if (ShowHiddenValue)
|
if (ShowHiddenValue)
|
||||||
{
|
{
|
||||||
|
|||||||
52
src/App/Resources/AppResources.Designer.cs
generated
52
src/App/Resources/AppResources.Designer.cs
generated
@@ -1,7 +1,6 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
// Runtime Version:4.0.30319.42000
|
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
@@ -10,7 +9,6 @@
|
|||||||
|
|
||||||
namespace Bit.App.Resources {
|
namespace Bit.App.Resources {
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
|
|
||||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
@@ -3371,6 +3369,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AddASend {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AddASend", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string CopyLink {
|
public static string CopyLink {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("CopyLink", resourceCulture);
|
return ResourceManager.GetString("CopyLink", resourceCulture);
|
||||||
@@ -3484,5 +3488,49 @@ namespace Bit.App.Resources {
|
|||||||
return ResourceManager.GetString("AboutSend", resourceCulture);
|
return ResourceManager.GetString("AboutSend", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string HideEmail {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("HideEmail", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SendOptionsPolicyInEffect {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SendOptionsPolicyInEffect", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SendFilePremiumRequired {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SendFilePremiumRequired", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SendFileEmailVerificationRequired {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SendFileEmailVerificationRequired", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string PasswordPrompt
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ResourceManager.GetString("PasswordPrompt", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static string PasswordConfirmation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ResourceManager.GetString("PasswordConfirmation", resourceCulture);
|
||||||
|
}
|
||||||
|
}public static string PasswordConfirmationDesc
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ResourceManager.GetString("PasswordConfirmationDesc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1901,7 +1901,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>There are no Sends in your account.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1972,4 +1976,28 @@
|
|||||||
<value>About Send</value>
|
<value>About Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Hide my email address from recipients.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>One or more organization policies are affecting your Send options.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Free accounts are restricted to sharing text only. A premium membership is required to use files with Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>You must verify your email to use files with Send. You can verify your email in the web vault.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1900,7 +1900,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>There are no Sends in your account.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1971,4 +1975,28 @@
|
|||||||
<value>About Send</value>
|
<value>About Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Hide my email address from recipients.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>One or more organization policies are affecting your Send options.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Free accounts are restricted to sharing text only. A premium membership is required to use files with Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>You must verify your email to use files with Send. You can verify your email in the web vault.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1603,10 +1603,10 @@
|
|||||||
<value>Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него.</value>
|
<value>Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportKeyWarning" xml:space="preserve">
|
<data name="EncExportKeyWarning" xml:space="preserve">
|
||||||
<value>This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file.</value>
|
<value>При изнасяне данните се шифрират с ключа ви. Ако го смените, ще трябва наново да ги изнесете, защото няма да може да дешифрирате настоящия файл.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportAccountWarning" xml:space="preserve">
|
<data name="EncExportAccountWarning" xml:space="preserve">
|
||||||
<value>Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account.</value>
|
<value>Ключовете за шифриране са уникални за всеки потребител, затова не може да внесете шифрирани данни от един потребител в регистрацията на друг.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
||||||
<value>Потвърждаване на изнасянето на трезора</value>
|
<value>Потвърждаване на изнасянето на трезора</value>
|
||||||
@@ -1802,173 +1802,201 @@
|
|||||||
<value>Политика от някоя организация влияе на вариантите за собственост.</value>
|
<value>Политика от някоя организация влияе на вариантите за собственост.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Send" xml:space="preserve">
|
<data name="Send" xml:space="preserve">
|
||||||
<value>Send</value>
|
<value>Изпращане</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllSends" xml:space="preserve">
|
<data name="AllSends" xml:space="preserve">
|
||||||
<value>All Sends</value>
|
<value>Всички изпращания</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Sends" xml:space="preserve">
|
<data name="Sends" xml:space="preserve">
|
||||||
<value>Sends</value>
|
<value>Изпращания</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NameInfo" xml:space="preserve">
|
<data name="NameInfo" xml:space="preserve">
|
||||||
<value>A friendly name to describe this Send.</value>
|
<value>Описателно име за това изпращане.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeText" xml:space="preserve">
|
<data name="TypeText" xml:space="preserve">
|
||||||
<value>Text</value>
|
<value>Текст</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeTextInfo" xml:space="preserve">
|
<data name="TypeTextInfo" xml:space="preserve">
|
||||||
<value>The text you want to send.</value>
|
<value>Текст за изпращане.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="HideTextByDefault" xml:space="preserve">
|
<data name="HideTextByDefault" xml:space="preserve">
|
||||||
<value>When accessing the Send, hide the text by default</value>
|
<value>При достъп до изпращането стандартно текстът да се скрива</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFile" xml:space="preserve">
|
<data name="TypeFile" xml:space="preserve">
|
||||||
<value>File</value>
|
<value>Файл</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>The file you want to send.</value>
|
<value>Файл за изпращане.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Deletion Date</value>
|
<value>Дата на изтриване</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionTime" xml:space="preserve">
|
<data name="DeletionTime" xml:space="preserve">
|
||||||
<value>Deletion Time</value>
|
<value>Време на изтриване</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>The Send will be permanently deleted on the specified date and time.</value>
|
<value>Изпращането ще бъде окончателно изтрито на зададената дата и време.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PendingDelete" xml:space="preserve">
|
<data name="PendingDelete" xml:space="preserve">
|
||||||
<value>Pending deletion</value>
|
<value>Предстои изтриване</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDate" xml:space="preserve">
|
<data name="ExpirationDate" xml:space="preserve">
|
||||||
<value>Expiration Date</value>
|
<value>Дата на валидност</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationTime" xml:space="preserve">
|
<data name="ExpirationTime" xml:space="preserve">
|
||||||
<value>Expiration Time</value>
|
<value>Време на валидност</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>If set, access to this Send will expire on the specified date and time.</value>
|
<value>При задаване — това изпращане ще се изключи на зададената дата и време.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Expired" xml:space="preserve">
|
<data name="Expired" xml:space="preserve">
|
||||||
<value>Expired</value>
|
<value>Изтекъл</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCount" xml:space="preserve">
|
<data name="MaximumAccessCount" xml:space="preserve">
|
||||||
<value>Maximum Access Count</value>
|
<value>Максимален брой достъпвания.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||||
<value>If set, users will no longer be able to access this send once the maximum access count is reached.</value>
|
<value>При задаване — това изпращане ще се изключи след определен брой достъпвания.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountReached" xml:space="preserve">
|
<data name="MaximumAccessCountReached" xml:space="preserve">
|
||||||
<value>Max access count reached</value>
|
<value>Достигнат е максималният брой достъпвания</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CurrentAccessCount" xml:space="preserve">
|
<data name="CurrentAccessCount" xml:space="preserve">
|
||||||
<value>Current Access Count</value>
|
<value>Текущ брой на достъпванията</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewPassword" xml:space="preserve">
|
<data name="NewPassword" xml:space="preserve">
|
||||||
<value>New Password</value>
|
<value>Нова парола</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordInfo" xml:space="preserve">
|
<data name="PasswordInfo" xml:space="preserve">
|
||||||
<value>Optionally require a password for users to access this Send.</value>
|
<value>Изискване на парола за достъп до това изпращане.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovePassword" xml:space="preserve">
|
<data name="RemovePassword" xml:space="preserve">
|
||||||
<value>Remove Password</value>
|
<value>Премахване на парола</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||||
<value>Are you sure you want to remove the password?</value>
|
<value>Сигурни ли сте, че искате да премахнете паролата?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovingSendPassword" xml:space="preserve">
|
<data name="RemovingSendPassword" xml:space="preserve">
|
||||||
<value>Removing password</value>
|
<value>Премахване на парола</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||||
<value>Password has been removed.</value>
|
<value>Паролата е премахната.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NotesInfo" xml:space="preserve">
|
<data name="NotesInfo" xml:space="preserve">
|
||||||
<value>Private notes about this Send.</value>
|
<value>Скрити бележки за това изпращане.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisableSend" xml:space="preserve">
|
<data name="DisableSend" xml:space="preserve">
|
||||||
<value>Disable this Send so that no one can access it.</value>
|
<value>Пълно спиране на това изпращане — никой няма да има достъп.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>Няма изпращания.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
<value>Copy Link</value>
|
<value>Копиране на връзката</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareLink" xml:space="preserve">
|
<data name="ShareLink" xml:space="preserve">
|
||||||
<value>Share Link</value>
|
<value>Споделяне на връзката</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendLink" xml:space="preserve">
|
<data name="SendLink" xml:space="preserve">
|
||||||
<value>Send link</value>
|
<value>Изпращане на връзката</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SearchSends" xml:space="preserve">
|
<data name="SearchSends" xml:space="preserve">
|
||||||
<value>Search Sends</value>
|
<value>Търсене в изпратените</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="EditSend" xml:space="preserve">
|
<data name="EditSend" xml:space="preserve">
|
||||||
<value>Edit Send</value>
|
<value>Редактиране на изпращане</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AddSend" xml:space="preserve">
|
<data name="AddSend" xml:space="preserve">
|
||||||
<value>Add Send</value>
|
<value>Добавяне на изпращане</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
||||||
<value>Are you sure you want to delete this Send?</value>
|
<value>Сигурни ли сте, че искате да изтриете това изпращане?</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDeleted" xml:space="preserve">
|
<data name="SendDeleted" xml:space="preserve">
|
||||||
<value>Send has been deleted.</value>
|
<value>Изпратеното е изтрито.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendUpdated" xml:space="preserve">
|
<data name="SendUpdated" xml:space="preserve">
|
||||||
<value>Send updated.</value>
|
<value>Изпратеното е редактирано.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewSendCreated" xml:space="preserve">
|
<data name="NewSendCreated" xml:space="preserve">
|
||||||
<value>New send created.</value>
|
<value>Създадено е ново изпращане.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="OneDay" xml:space="preserve">
|
<data name="OneDay" xml:space="preserve">
|
||||||
<value>1 day</value>
|
<value>1 ден</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoDays" xml:space="preserve">
|
<data name="TwoDays" xml:space="preserve">
|
||||||
<value>2 days</value>
|
<value>2 дни</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThreeDays" xml:space="preserve">
|
<data name="ThreeDays" xml:space="preserve">
|
||||||
<value>3 days</value>
|
<value>3 дни</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SevenDays" xml:space="preserve">
|
<data name="SevenDays" xml:space="preserve">
|
||||||
<value>7 days</value>
|
<value>7 дни</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThirtyDays" xml:space="preserve">
|
<data name="ThirtyDays" xml:space="preserve">
|
||||||
<value>30 days</value>
|
<value>30 дни</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Custom" xml:space="preserve">
|
<data name="Custom" xml:space="preserve">
|
||||||
<value>Custom</value>
|
<value>По избор</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareOnSave" xml:space="preserve">
|
<data name="ShareOnSave" xml:space="preserve">
|
||||||
<value>Share this Send upon save.</value>
|
<value>Споделяне на изпратеното при запис.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDisabledWarning" xml:space="preserve">
|
<data name="SendDisabledWarning" xml:space="preserve">
|
||||||
<value>Due to an enterprise policy, you are only able to delete an existing Send.</value>
|
<value>Поради политика на организация, може само да изтривате съществуващи изпращания.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AboutSend" xml:space="preserve">
|
<data name="AboutSend" xml:space="preserve">
|
||||||
<value>About Send</value>
|
<value>Относно изпращането</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Скриване на е-пощата ми от получателите.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Поне една политика на организация влияе на настройките за изпращане.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Безплатните регистрации могат да споделят само текст. Необходим е платен абонамент, за да можете да изпращате файлове.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>Трябва да потвърдите е-пощата си, за да можете да изпращате файлове.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1901,7 +1901,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>There are no Sends in your account.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1972,4 +1976,28 @@
|
|||||||
<value>About Send</value>
|
<value>About Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Hide my email address from recipients.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>One or more organization policies are affecting your Send options.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Free accounts are restricted to sharing text only. A premium membership is required to use files with Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>You must verify your email to use files with Send. You can verify your email in the web vault.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
2003
src/App/Resources/AppResources.bs.resx
Normal file
2003
src/App/Resources/AppResources.bs.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1903,6 +1903,10 @@
|
|||||||
<value>No hi ha cap enviament al vostre compte.</value>
|
<value>No hi ha cap enviament al vostre compte.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
<value>Copia l'enllaç</value>
|
<value>Copia l'enllaç</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1971,4 +1975,28 @@
|
|||||||
<value>Quant a Send</value>
|
<value>Quant a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Amagueu la meua adreça de correu electrònic als destinataris.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Una o més polítiques d'organització afecten les vostres opcions del Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Els comptes gratuïts només es poden compartir text. Cal utilitzar una subscripció premium per utilitzar fitxers amb Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>Heu de verificar el vostre correu electrònic per utilitzar aquesta característica.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1814,7 +1814,7 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NameInfo" xml:space="preserve">
|
<data name="NameInfo" xml:space="preserve">
|
||||||
<value>A friendly name to describe this Send.</value>
|
<value>Přátelský název pro popis tohoto Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeText" xml:space="preserve">
|
<data name="TypeText" xml:space="preserve">
|
||||||
@@ -1840,7 +1840,7 @@
|
|||||||
<value>Čas odstranění</value>
|
<value>Čas odstranění</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>The Send will be permanently deleted on the specified date and time.</value>
|
<value>Tento Send bude trvale smazán v určený datum a čas.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PendingDelete" xml:space="preserve">
|
<data name="PendingDelete" xml:space="preserve">
|
||||||
@@ -1853,7 +1853,7 @@
|
|||||||
<value>Čas expirace</value>
|
<value>Čas expirace</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>Je-li nastaveno, přístup k tomuto Send vyprší k danému datumu a času.</value>
|
<value>Je-li nastaveno, přístup k tomuto Send vyprší v daný datum a čas.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Expired" xml:space="preserve">
|
<data name="Expired" xml:space="preserve">
|
||||||
@@ -1876,7 +1876,7 @@
|
|||||||
<value>Nové heslo</value>
|
<value>Nové heslo</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordInfo" xml:space="preserve">
|
<data name="PasswordInfo" xml:space="preserve">
|
||||||
<value>Volitelně vyžaduje heslo pro přístup k tomuto Send.</value>
|
<value>Volitelně vyžadovat heslo pro přístup k tomuto Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovePassword" xml:space="preserve">
|
<data name="RemovePassword" xml:space="preserve">
|
||||||
@@ -1896,11 +1896,15 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisableSend" xml:space="preserve">
|
<data name="DisableSend" xml:space="preserve">
|
||||||
<value>Disable this Send so that no one can access it.</value>
|
<value>Zakažte tento Send, aby k němu nikdo neměl přístup.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>Ve vašem účtu nejsou žádné Sends.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1910,7 +1914,7 @@
|
|||||||
<value>Sdílet odkaz</value>
|
<value>Sdílet odkaz</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendLink" xml:space="preserve">
|
<data name="SendLink" xml:space="preserve">
|
||||||
<value>Odeslat odkaz</value>
|
<value>Odkaz tohoto Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SearchSends" xml:space="preserve">
|
<data name="SearchSends" xml:space="preserve">
|
||||||
@@ -1960,15 +1964,39 @@
|
|||||||
<value>Vlastní</value>
|
<value>Vlastní</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareOnSave" xml:space="preserve">
|
<data name="ShareOnSave" xml:space="preserve">
|
||||||
<value>Share this Send upon save.</value>
|
<value>Sdílet tento Send po uložení.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDisabledWarning" xml:space="preserve">
|
<data name="SendDisabledWarning" xml:space="preserve">
|
||||||
<value>Due to an enterprise policy, you are only able to delete an existing Send.</value>
|
<value>Kvůli zásadám podniku můžete odstranit pouze existující Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AboutSend" xml:space="preserve">
|
<data name="AboutSend" xml:space="preserve">
|
||||||
<value>About Send</value>
|
<value>O Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Skrýt mou e-mailovou adresu před příjemci.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Jedna nebo více zásad organizace ovlivňuje nastavení Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Bezplatné účty jsou omezeny pouze na sdílení textu. K používání souborů pomocí Send je vyžadováno prémiové členství.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>K využívání souborů s pomocí Send je zapotřebí ověření vašeho e-mailu.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1603,7 +1603,7 @@
|
|||||||
<value>Denne eksport indeholder dine boksdata i en ukrypteret form. Du bør ikke gemme eller sende den eksporterede fil via usikre kanaler (f.eks. e-mail). Slet den straks efter at du er færdig med at bruge den.</value>
|
<value>Denne eksport indeholder dine boksdata i en ukrypteret form. Du bør ikke gemme eller sende den eksporterede fil via usikre kanaler (f.eks. e-mail). Slet den straks efter at du er færdig med at bruge den.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportKeyWarning" xml:space="preserve">
|
<data name="EncExportKeyWarning" xml:space="preserve">
|
||||||
<value>Denne eksport krypterer dine data vha. din kontos krypteringsnøgle. Roterer du på et tidspunkt denne kontokrypteringsnøgle, skal du eksportere igen, da du ikke vil kunne dekryptere denne eksportfil.</value>
|
<value>Denne eksport krypterer dine data vha. din kontos krypteringsnøgle. Roterer du på et tidspunkt denne krypteringsnøgle, skal du eksportere igen, da du ikke vil kunne dekryptere denne eksportfil.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportAccountWarning" xml:space="preserve">
|
<data name="EncExportAccountWarning" xml:space="preserve">
|
||||||
<value>Kontokrypteringsnøgler er unikke for hver Bitwarden-brugerkonto, så du kan ikke importere en krypteret eksport til en anden konto.</value>
|
<value>Kontokrypteringsnøgler er unikke for hver Bitwarden-brugerkonto, så du kan ikke importere en krypteret eksport til en anden konto.</value>
|
||||||
@@ -1831,7 +1831,7 @@
|
|||||||
<value>Fil</value>
|
<value>Fil</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>Filen, du vil sende.</value>
|
<value>Den fil, du vil sende.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Sletningsdato</value>
|
<value>Sletningsdato</value>
|
||||||
@@ -1840,7 +1840,7 @@
|
|||||||
<value>Sletningstidspunkt</value>
|
<value>Sletningstidspunkt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>Send'en slettes permanent på angivne dato og tidspunkt.</value>
|
<value>Send'en slettes permanent på den angivne dato og tid.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PendingDelete" xml:space="preserve">
|
<data name="PendingDelete" xml:space="preserve">
|
||||||
@@ -1853,21 +1853,21 @@
|
|||||||
<value>Udløbstidspunkt</value>
|
<value>Udløbstidspunkt</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>Hvis opsat, udløber adgangen til denne Send på angivne dato og tidspunkt.</value>
|
<value>Hvis angivet, vil adgangen til denne Send udløbe på den angivne dato og tidspunkt.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Expired" xml:space="preserve">
|
<data name="Expired" xml:space="preserve">
|
||||||
<value>Udløbet</value>
|
<value>Udløbet</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCount" xml:space="preserve">
|
<data name="MaximumAccessCount" xml:space="preserve">
|
||||||
<value>Maks. antal tilgange</value>
|
<value>Maksimalt antal tilgange</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||||
<value>Hvis opsat, vil brugere ikke længere kunne tilgå denne Send, når maks. adgangsantal er nået.</value>
|
<value>Hvis opsat, vil brugere ikke længere kunne tilgå denne Send, når det maksimale adgangsantal er nået.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountReached" xml:space="preserve">
|
<data name="MaximumAccessCountReached" xml:space="preserve">
|
||||||
<value>Maks. adgangsantal nået</value>
|
<value>Maksimalt adgangsantal nået</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CurrentAccessCount" xml:space="preserve">
|
<data name="CurrentAccessCount" xml:space="preserve">
|
||||||
<value>Aktuelt antal tilgange</value>
|
<value>Aktuelt antal tilgange</value>
|
||||||
@@ -1883,16 +1883,16 @@
|
|||||||
<value>Fjern adgangskode</value>
|
<value>Fjern adgangskode</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||||
<value>Sikker på, at du vil fjerne adgangskoden?</value>
|
<value>Er du sikker på, at du vil fjerne adgangskoden?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovingSendPassword" xml:space="preserve">
|
<data name="RemovingSendPassword" xml:space="preserve">
|
||||||
<value>Fjerner adgangskode</value>
|
<value>Fjerner adgangskode</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||||
<value>Adgangskode er fjernet</value>
|
<value>Adgangskoden er fjernet.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NotesInfo" xml:space="preserve">
|
<data name="NotesInfo" xml:space="preserve">
|
||||||
<value>Fortrolige notater om denne Send.</value>
|
<value>Private notater om denne Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisableSend" xml:space="preserve">
|
<data name="DisableSend" xml:space="preserve">
|
||||||
@@ -1903,6 +1903,10 @@
|
|||||||
<value>Der er ingen Sends i din konto.</value>
|
<value>Der er ingen Sends i din konto.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
<value>Kopiér link</value>
|
<value>Kopiér link</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1914,7 +1918,7 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SearchSends" xml:space="preserve">
|
<data name="SearchSends" xml:space="preserve">
|
||||||
<value>Find Sends</value>
|
<value>Søg i Sends</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="EditSend" xml:space="preserve">
|
<data name="EditSend" xml:space="preserve">
|
||||||
@@ -1926,11 +1930,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
||||||
<value>Sikker på, at du vil slette denne Send?</value>
|
<value>Er du sikker på, at du vil slette denne Send?</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDeleted" xml:space="preserve">
|
<data name="SendDeleted" xml:space="preserve">
|
||||||
<value>Element er slettet.</value>
|
<value>Send er blevet slettet.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendUpdated" xml:space="preserve">
|
<data name="SendUpdated" xml:space="preserve">
|
||||||
@@ -1971,4 +1975,28 @@
|
|||||||
<value>Om Send</value>
|
<value>Om Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Skjul min e-mailadresse for modtagere.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Én eller flere organisationspolitikker påvirker dine Send-valgmuligheder.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Gratis konti er begrænsede til tekstdeling alene. Et Premium-medlemskab kræves for a bruge filer med Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>Du skal bekræfte din e-mail for brug af filer med Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1603,10 +1603,10 @@
|
|||||||
<value>Dieser Export enthält deine Tresordaten in einem unverschlüsseltem Format. Du solltest sie nicht speichern oder über unsichere Kanäle (z. B. E-Mail) senden. Lösche sie sofort nach ihrer Verwendung.</value>
|
<value>Dieser Export enthält deine Tresordaten in einem unverschlüsseltem Format. Du solltest sie nicht speichern oder über unsichere Kanäle (z. B. E-Mail) senden. Lösche sie sofort nach ihrer Verwendung.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportKeyWarning" xml:space="preserve">
|
<data name="EncExportKeyWarning" xml:space="preserve">
|
||||||
<value>Dieser Export verschlüsselt Ihre Daten mit dem Verschlüsselungscode Ihres Kontos. Falls Sie Ihren Verschlüsselungscode erneuern, sollten Sie den Export erneut durchführen, da Sie die zuvor erstellte Datei ansonsten nicht mehr entschlüsseln können.</value>
|
<value>Dieser Export verschlüsselt deine Daten mit dem Verschlüsselungscode deines Kontos. Falls du deinen Verschlüsselungscode erneuerst, solltest du den Export erneut durchführen, da du die zuvor erstellte Datei ansonsten nicht mehr entschlüsseln kannst.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportAccountWarning" xml:space="preserve">
|
<data name="EncExportAccountWarning" xml:space="preserve">
|
||||||
<value>Die Verschlüsselungscodes eines Kontos sind für jedes Bitwarden Benutzerkonto einzigartig, deshalb können Sie keinen verschlüsselten Export in ein anderes Konto importieren.</value>
|
<value>Die Verschlüsselungscodes eines Kontos sind für jedes Bitwarden Benutzerkonto einzigartig, deshalb kannst du keinen verschlüsselten Export in ein anderes Konto importieren.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
||||||
<value>Tresor-Export bestätigen</value>
|
<value>Tresor-Export bestätigen</value>
|
||||||
@@ -1821,17 +1821,17 @@
|
|||||||
<value>Text</value>
|
<value>Text</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeTextInfo" xml:space="preserve">
|
<data name="TypeTextInfo" xml:space="preserve">
|
||||||
<value>Der Text, den Sie senden möchten.</value>
|
<value>Der Text, den du senden möchtest.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="HideTextByDefault" xml:space="preserve">
|
<data name="HideTextByDefault" xml:space="preserve">
|
||||||
<value>Beim Zugriff auf dieses Send den Text standardmäßig ausblenden</value>
|
<value>Beim Zugriff auf diesen Send den Text standardmäßig ausblenden</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFile" xml:space="preserve">
|
<data name="TypeFile" xml:space="preserve">
|
||||||
<value>Datei</value>
|
<value>Datei</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>Die Datei, die Sie senden möchten.</value>
|
<value>Die Datei, die du senden möchtest.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Löschdatum</value>
|
<value>Löschdatum</value>
|
||||||
@@ -1850,10 +1850,10 @@
|
|||||||
<value>Ablaufdatum</value>
|
<value>Ablaufdatum</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationTime" xml:space="preserve">
|
<data name="ExpirationTime" xml:space="preserve">
|
||||||
<value>Verfallszeit</value>
|
<value>Ablaufzeit</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>Falls aktiviert, verfällt der Zugriff auf dieses Send am angegebenen Datum zur angegebenen Uhrzeit.</value>
|
<value>Falls aktiviert, verfällt der Zugriff auf diesen Send zur angegebenen Datum und Uhrzeit.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Expired" xml:space="preserve">
|
<data name="Expired" xml:space="preserve">
|
||||||
@@ -1863,7 +1863,7 @@
|
|||||||
<value>Maximale Zugriffsanzahl</value>
|
<value>Maximale Zugriffsanzahl</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||||
<value>Falls aktiviert, können Benutzer nicht mehr auf dieses Send zugreifen, sobald die maximale Zugriffsanzahl erreicht ist.</value>
|
<value>Falls aktiviert, können Benutzer nicht mehr auf diesesn Send zugreifen, sobald die maximale Zugriffsanzahl erreicht ist.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountReached" xml:space="preserve">
|
<data name="MaximumAccessCountReached" xml:space="preserve">
|
||||||
@@ -1883,7 +1883,7 @@
|
|||||||
<value>Passwort entfernen</value>
|
<value>Passwort entfernen</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||||
<value>Sind Sie sicher, dass Sie das Passwort entfernen möchten?</value>
|
<value>Bist du sicher, dass du dieses Passwort entfernen möchtest?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovingSendPassword" xml:space="preserve">
|
<data name="RemovingSendPassword" xml:space="preserve">
|
||||||
<value>Passwort wird entfernt</value>
|
<value>Passwort wird entfernt</value>
|
||||||
@@ -1900,7 +1900,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>Es gibt keine Sends in Ihrem Konto.</value>
|
<value>Es gibt keine Sends in deinem Konto.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1926,7 +1930,7 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
||||||
<value>Sind Sie sicher, dass Sie dieses Send löschen möchten?</value>
|
<value>Bist du sicher, dass du dieses Send löschen möchtest?</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDeleted" xml:space="preserve">
|
<data name="SendDeleted" xml:space="preserve">
|
||||||
@@ -1964,11 +1968,35 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDisabledWarning" xml:space="preserve">
|
<data name="SendDisabledWarning" xml:space="preserve">
|
||||||
<value>Aufgrund einer Unternehmensrichtlinie können Sie nur ein bestehendes Send löschen.</value>
|
<value>Aufgrund einer Unternehmensrichtlinie kannst du nur ein bestehendes Send löschen.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AboutSend" xml:space="preserve">
|
<data name="AboutSend" xml:space="preserve">
|
||||||
<value>Über Send</value>
|
<value>Über Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Meine E-Mail-Adresse vor den Empfängern ausblenden.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Eine oder mehrere Organisationsrichtlinien beeinflussen deine Send Einstellungen.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Kostenlose Konten sind auf das Teilen von Texten beschränkt. Eine Premium-Mitgliedschaft ist erforderlich, um Dateien mit Send zu verwenden.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>Du musst deine E-Mail verifizieren, um diese Funktion nutzen zu können.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1604,10 +1604,10 @@
|
|||||||
<value>Αυτή η εξαγωγή περιέχει τα δεδομένα σε μη κρυπτογραφημένη μορφή. Δεν πρέπει να αποθηκεύετε ή να στείλετε το εξαγόμενο αρχείο μέσω μη ασφαλών τρόπων (όπως μέσω email). Διαγράψτε το αμέσως μόλις τελειώσετε με τη χρήση του.</value>
|
<value>Αυτή η εξαγωγή περιέχει τα δεδομένα σε μη κρυπτογραφημένη μορφή. Δεν πρέπει να αποθηκεύετε ή να στείλετε το εξαγόμενο αρχείο μέσω μη ασφαλών τρόπων (όπως μέσω email). Διαγράψτε το αμέσως μόλις τελειώσετε με τη χρήση του.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportKeyWarning" xml:space="preserve">
|
<data name="EncExportKeyWarning" xml:space="preserve">
|
||||||
<value>This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file.</value>
|
<value>Αυτή η εξαγωγή κρυπτογραφεί τα δεδομένα σας χρησιμοποιώντας το κλειδί κρυπτογράφησης του λογαριασμού σας. Εάν ποτέ περιστρέψετε το κλειδί κρυπτογράφησης του λογαριασμού σας, θα πρέπει να κάνετε εξαγωγή ξανά, καθώς δεν θα μπορείτε να αποκρυπτογραφήσετε αυτό το αρχείο εξαγωγής.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportAccountWarning" xml:space="preserve">
|
<data name="EncExportAccountWarning" xml:space="preserve">
|
||||||
<value>Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account.</value>
|
<value>Τα κλειδιά κρυπτογράφησης λογαριασμού είναι μοναδικά για κάθε λογαριασμό χρήστη Bitwarden, οπότε δεν μπορείτε να εισάγετε μια κρυπτογραφημένη εξαγωγή σε διαφορετικό λογαριασμό.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
||||||
<value>Επιβεβαίωση εξαγωγής Vault</value>
|
<value>Επιβεβαίωση εξαγωγής Vault</value>
|
||||||
@@ -1807,7 +1807,7 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllSends" xml:space="preserve">
|
<data name="AllSends" xml:space="preserve">
|
||||||
<value>All Sends</value>
|
<value>Όλα τα Sends</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Sends" xml:space="preserve">
|
<data name="Sends" xml:space="preserve">
|
||||||
@@ -1815,161 +1815,189 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NameInfo" xml:space="preserve">
|
<data name="NameInfo" xml:space="preserve">
|
||||||
<value>A friendly name to describe this Send.</value>
|
<value>Ένα φιλικό όνομα για την περιγραφή αυτού του Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeText" xml:space="preserve">
|
<data name="TypeText" xml:space="preserve">
|
||||||
<value>Text</value>
|
<value>Κείμενο</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeTextInfo" xml:space="preserve">
|
<data name="TypeTextInfo" xml:space="preserve">
|
||||||
<value>The text you want to send.</value>
|
<value>Το κείμενο που θέλετε να στείλετε.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="HideTextByDefault" xml:space="preserve">
|
<data name="HideTextByDefault" xml:space="preserve">
|
||||||
<value>When accessing the Send, hide the text by default</value>
|
<value>Κατά την πρόσβαση σε αυτό το Send, αποκρύψτε το κείμενο από προεπιλογή</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFile" xml:space="preserve">
|
<data name="TypeFile" xml:space="preserve">
|
||||||
<value>File</value>
|
<value>Αρχείο</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>The file you want to send.</value>
|
<value>Το αρχείο που θέλετε να στείλετε.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Deletion Date</value>
|
<value>Ημερομηνία διαγραφής</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionTime" xml:space="preserve">
|
<data name="DeletionTime" xml:space="preserve">
|
||||||
<value>Deletion Time</value>
|
<value>Χρόνος Διαγραφής</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>The Send will be permanently deleted on the specified date and time.</value>
|
<value>Το Send θα διαγραφεί οριστικά την καθορισμένη ημερομηνία και ώρα.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PendingDelete" xml:space="preserve">
|
<data name="PendingDelete" xml:space="preserve">
|
||||||
<value>Pending deletion</value>
|
<value>Εκκρεμεί διαγραφή</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDate" xml:space="preserve">
|
<data name="ExpirationDate" xml:space="preserve">
|
||||||
<value>Expiration Date</value>
|
<value>Ημερομηνία Λήξης</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationTime" xml:space="preserve">
|
<data name="ExpirationTime" xml:space="preserve">
|
||||||
<value>Expiration Time</value>
|
<value>Χρόνος λήξης</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>If set, access to this Send will expire on the specified date and time.</value>
|
<value>Εάν οριστεί, η πρόσβαση σε αυτό το Send θα λήξει την καθορισμένη ημερομηνία και ώρα.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Expired" xml:space="preserve">
|
<data name="Expired" xml:space="preserve">
|
||||||
<value>Expired</value>
|
<value>Έληξε</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCount" xml:space="preserve">
|
<data name="MaximumAccessCount" xml:space="preserve">
|
||||||
<value>Maximum Access Count</value>
|
<value>Μέγιστος Αριθμός Πρόσβασης</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||||
<value>If set, users will no longer be able to access this send once the maximum access count is reached.</value>
|
<value>Εάν οριστεί, οι χρήστες δεν θα μπορούν πλέον να έχουν πρόσβαση σε αυτό το send μόλις επιτευχθεί ο μέγιστος αριθμός πρόσβασης.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountReached" xml:space="preserve">
|
<data name="MaximumAccessCountReached" xml:space="preserve">
|
||||||
<value>Max access count reached</value>
|
<value>Φτάσατε στον μέγιστο αριθμό πρόσβασης</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CurrentAccessCount" xml:space="preserve">
|
<data name="CurrentAccessCount" xml:space="preserve">
|
||||||
<value>Current Access Count</value>
|
<value>Τρέχων Αριθμός Πρόσβασης</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewPassword" xml:space="preserve">
|
<data name="NewPassword" xml:space="preserve">
|
||||||
<value>New Password</value>
|
<value>Νέος Κωδικός</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordInfo" xml:space="preserve">
|
<data name="PasswordInfo" xml:space="preserve">
|
||||||
<value>Optionally require a password for users to access this Send.</value>
|
<value>Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovePassword" xml:space="preserve">
|
<data name="RemovePassword" xml:space="preserve">
|
||||||
<value>Remove Password</value>
|
<value>Αφαίρεση κωδικού</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||||
<value>Are you sure you want to remove the password?</value>
|
<value>Είστε βέβαιοι ότι θέλετε να καταργήσετε τον κωδικό πρόσβασης;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovingSendPassword" xml:space="preserve">
|
<data name="RemovingSendPassword" xml:space="preserve">
|
||||||
<value>Removing password</value>
|
<value>Αφαίρεση κωδικού...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||||
<value>Password has been removed.</value>
|
<value>Ο κωδικός έχει αφαιρεθεί.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NotesInfo" xml:space="preserve">
|
<data name="NotesInfo" xml:space="preserve">
|
||||||
<value>Private notes about this Send.</value>
|
<value>Ιδιωτικές σημειώσεις σχετικά με αυτό το Send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisableSend" xml:space="preserve">
|
<data name="DisableSend" xml:space="preserve">
|
||||||
<value>Disable this Send so that no one can access it.</value>
|
<value>Απενεργοποιήστε αυτό το Send έτσι ώστε κανείς να μην μπορεί να έχει πρόσβαση σε αυτό.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>Δεν υπάρχουν sends στο λογαριασμό σας.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
<value>Copy Link</value>
|
<value>Αντιγραφή Συνδέσμου</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareLink" xml:space="preserve">
|
<data name="ShareLink" xml:space="preserve">
|
||||||
<value>Share Link</value>
|
<value>Κοινοποίηση Συνδέσμου</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendLink" xml:space="preserve">
|
<data name="SendLink" xml:space="preserve">
|
||||||
<value>Send link</value>
|
<value>Σύνδεσμος Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SearchSends" xml:space="preserve">
|
<data name="SearchSends" xml:space="preserve">
|
||||||
<value>Search Sends</value>
|
<value>Αναζήτηση Sends</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="EditSend" xml:space="preserve">
|
<data name="EditSend" xml:space="preserve">
|
||||||
<value>Edit Send</value>
|
<value>Επεξεργασία Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AddSend" xml:space="preserve">
|
<data name="AddSend" xml:space="preserve">
|
||||||
<value>Add Send</value>
|
<value>Προσθήκη Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
<data name="AreYouSureDeleteSend" xml:space="preserve">
|
||||||
<value>Are you sure you want to delete this Send?</value>
|
<value>Είστε βέβαιοι ότι θέλετε να διαγράψετε το Send;</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDeleted" xml:space="preserve">
|
<data name="SendDeleted" xml:space="preserve">
|
||||||
<value>Send has been deleted.</value>
|
<value>Το send έχει διαγραφεί.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendUpdated" xml:space="preserve">
|
<data name="SendUpdated" xml:space="preserve">
|
||||||
<value>Send updated.</value>
|
<value>Το send ενημερώθηκε.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewSendCreated" xml:space="preserve">
|
<data name="NewSendCreated" xml:space="preserve">
|
||||||
<value>New send created.</value>
|
<value>Δημιουργήθηκε νέο send.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="OneDay" xml:space="preserve">
|
<data name="OneDay" xml:space="preserve">
|
||||||
<value>1 day</value>
|
<value>1 ημέρα</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoDays" xml:space="preserve">
|
<data name="TwoDays" xml:space="preserve">
|
||||||
<value>2 days</value>
|
<value>2 ημέρες</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThreeDays" xml:space="preserve">
|
<data name="ThreeDays" xml:space="preserve">
|
||||||
<value>3 days</value>
|
<value>3 ημέρες</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SevenDays" xml:space="preserve">
|
<data name="SevenDays" xml:space="preserve">
|
||||||
<value>7 days</value>
|
<value>7 ημέρες</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThirtyDays" xml:space="preserve">
|
<data name="ThirtyDays" xml:space="preserve">
|
||||||
<value>30 days</value>
|
<value>30 ημέρες</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Custom" xml:space="preserve">
|
<data name="Custom" xml:space="preserve">
|
||||||
<value>Custom</value>
|
<value>Προσαρμοσμένο</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareOnSave" xml:space="preserve">
|
<data name="ShareOnSave" xml:space="preserve">
|
||||||
<value>Share this Send upon save.</value>
|
<value>Κοινοποιήστε αυτό το send κατά την αποθήκευση.</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendDisabledWarning" xml:space="preserve">
|
<data name="SendDisabledWarning" xml:space="preserve">
|
||||||
<value>Due to an enterprise policy, you are only able to delete an existing Send.</value>
|
<value>Λόγω μιας επιχειρηματικής πολιτικής, είστε σε θέση να διαγράψετε μόνο ένα υπάρχον Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="AboutSend" xml:space="preserve">
|
<data name="AboutSend" xml:space="preserve">
|
||||||
<value>About Send</value>
|
<value>Σχετικά με το Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Απόκρυψη της διεύθυνσης email μου από τους παραλήπτες.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>Μία ή περισσότερες οργανωτικές πολιτικές επηρεάζουν τις επιλογές send σας.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Οι δωρεάν λογαριασμοί περιορίζονται μόνο στην κοινή χρήση κειμένου. Μια premium συνδρομή απαιτείται για τη χρήση αρχείων με Αποστολή.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>Πρέπει να επαληθεύσετε το email σας για να χρησιμοποιήσετε τα αρχεία με το send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1913,7 +1913,11 @@
|
|||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoSends" xml:space="preserve">
|
<data name="NoSends" xml:space="preserve">
|
||||||
<value>There are no sends in your account.</value>
|
<value>There are no Sends in your account.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="AddASend" xml:space="preserve">
|
||||||
|
<value>Add a Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="CopyLink" xml:space="preserve">
|
<data name="CopyLink" xml:space="preserve">
|
||||||
@@ -1984,4 +1988,28 @@
|
|||||||
<value>About Send</value>
|
<value>About Send</value>
|
||||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HideEmail" xml:space="preserve">
|
||||||
|
<value>Hide my email address from recipients.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendOptionsPolicyInEffect" xml:space="preserve">
|
||||||
|
<value>One or more organisation policies are affecting your Send options.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||||
|
<value>Free accounts are restricted to sharing text only. A premium membership is required to use files with Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="SendFileEmailVerificationRequired" xml:space="preserve">
|
||||||
|
<value>You must verify your email to use files with Send.</value>
|
||||||
|
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordPrompt" xml:space="preserve">
|
||||||
|
<value>Master password re-prompt</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmation" xml:space="preserve">
|
||||||
|
<value>Master password confirmation</value>
|
||||||
|
</data>
|
||||||
|
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
||||||
|
<value>This action is protected, to continue please re-enter your master password to verify your identity.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user