mirror of
https://github.com/bitwarden/mobile
synced 2026-01-19 08:53:17 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02ad4adff5 |
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@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up cloc
|
- name: Set up cloc
|
||||||
run: |
|
run: |
|
||||||
@@ -31,21 +31,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
uses: microsoft/setup-msbuild@v1
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help | grep Version
|
nuget help
|
||||||
msbuild -version
|
msbuild -version
|
||||||
dotnet --info
|
dotnet --info
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
Write-Output "GitHub ref: $env:GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
Write-Output "GitHub event: $env: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@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- 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'
|
||||||
@@ -82,14 +83,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@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
uses: actions/upload-artifact@v2
|
||||||
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@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.apk
|
name: com.x8bit.bitwarden.apk
|
||||||
path: ./com.x8bit.bitwarden.apk
|
path: ./com.x8bit.bitwarden.apk
|
||||||
@@ -114,7 +115,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@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
uses: actions/upload-artifact@v2
|
||||||
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
|
||||||
@@ -145,7 +146,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@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: '10.x'
|
||||||
|
|
||||||
@@ -180,7 +181,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
@@ -214,17 +215,18 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help | grep Version
|
nuget help
|
||||||
msbuild -version
|
msbuild -version
|
||||||
dotnet --info
|
dotnet --info
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
Write-Output "GitHub ref: $env:GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
Write-Output "GitHub event: $env: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@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
run: ./.github/scripts/ios/decrypt-secrets.ps1
|
run: ./.github/scripts/ios/decrypt-secrets.ps1
|
||||||
@@ -269,7 +271,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@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Bitwarden.ipa
|
name: Bitwarden.ipa
|
||||||
path: ./bitwarden-export/Bitwarden.ipa
|
path: ./bitwarden-export/Bitwarden.ipa
|
||||||
|
|||||||
187
.github/workflows/crowdin-sync.yml
vendored
187
.github/workflows/crowdin-sync.yml
vendored
@@ -1,187 +0,0 @@
|
|||||||
name: Crowdin Sync
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs: {}
|
|
||||||
#schedule:
|
|
||||||
# - cron: '0 0 * * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
crowdin-sync:
|
|
||||||
name: Autosync
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
env:
|
|
||||||
CROWDIN_BASE_URL: "https://api.crowdin.com/api/v2/projects"
|
|
||||||
CROWDIN_PROJECT_ID: "269690"
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
|
||||||
|
|
||||||
- name: Setup git config
|
|
||||||
run: |
|
|
||||||
git config user.name "github-actions"
|
|
||||||
git config user.email "<>"
|
|
||||||
|
|
||||||
- name: Get Crowndin Sync Branch
|
|
||||||
id: branch
|
|
||||||
run: |
|
|
||||||
BRANCH_NAME=crowdin-auto-sync
|
|
||||||
BRANCH_EXISTS=true
|
|
||||||
|
|
||||||
git fetch -a
|
|
||||||
git switch master
|
|
||||||
if [ $(git branch -a | egrep "remotes/origin/${BRANCH_NAME}$" | wc -l) -eq 0 ]; then
|
|
||||||
BRANCH_EXISTS=false
|
|
||||||
git switch -c $BRANCH_NAME
|
|
||||||
else
|
|
||||||
git switch $BRANCH_NAME
|
|
||||||
fi
|
|
||||||
git branch
|
|
||||||
|
|
||||||
echo "::set-output name=branch-exists::$BRANCH_EXISTS"
|
|
||||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
|
||||||
|
|
||||||
- name: Login to Azure
|
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Retrieve secrets
|
|
||||||
id: retrieve-secrets
|
|
||||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-prod-kv"
|
|
||||||
secrets: "crowdin-api-token"
|
|
||||||
|
|
||||||
- name: Get Crowdin master branch id
|
|
||||||
id: crowdin-master-branch
|
|
||||||
env:
|
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
|
||||||
run: |
|
|
||||||
# Step 1: GET master branchId
|
|
||||||
BRANCH_ID=$(
|
|
||||||
curl -s -H "Authorization: Bearer $CROWDIN_API_TOKEN" \
|
|
||||||
$CROWDIN_BASE_URL/$CROWDIN_PROJECT_ID/branches | jq -r '.data[0].data.id'
|
|
||||||
)
|
|
||||||
echo "[*] Crowin master branch id: $BRANCH_ID"
|
|
||||||
echo "::set-output name=id::$BRANCH_ID"
|
|
||||||
|
|
||||||
- name: Get Crowdin build id
|
|
||||||
id: crowdin-build
|
|
||||||
env:
|
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
|
||||||
CROWDIN_MASTER_BRANCH_ID: ${{ steps.crowdin-master-branch.outputs.id }}
|
|
||||||
run: |
|
|
||||||
# Step 2: POST Build the translations and get store build id
|
|
||||||
BUILD_ID=$(
|
|
||||||
curl -X POST -s \
|
|
||||||
-H "Authorization: Bearer $CROWDIN_API_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
$CROWDIN_BASE_URL/$CROWDIN_PROJECT_ID/translations/builds \
|
|
||||||
-d "{\"branchId\": $CROWDIN_MASTER_BRANCH_ID}" | jq -r '.data.id'
|
|
||||||
)
|
|
||||||
echo "[*] Crowin translations build id: $BRANCH_ID"
|
|
||||||
echo "::set-output name=id::$BUILD_ID"
|
|
||||||
|
|
||||||
- name: Wait for Crowdin build to finish
|
|
||||||
env:
|
|
||||||
MAX_TRIES: 12
|
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
|
||||||
CROWDIN_BUILD_ID: ${{ steps.crowdin-build.outputs.id }}
|
|
||||||
run: |
|
|
||||||
for try in {1..$MAX_TRIES}; do
|
|
||||||
BRANCH_STATUS=$(
|
|
||||||
curl -s -H "Authorization: Bearer $CROWDIN_API_TOKEN" \
|
|
||||||
$CROWDIN_BASE_URL/$CROWDIN_PROJECT_ID/translations/builds/$CROWDIN_BUILD_ID | jq -r '.data.status'
|
|
||||||
)
|
|
||||||
echo "[*] Build status: $BRANCH_STATUS"
|
|
||||||
if [[ "$BRANCH_STATUS" == "finished" ]]; then break; fi
|
|
||||||
if [[ "$try" == "$MAX_TRIES" ]]; then
|
|
||||||
echo "[!] Exceeded tries: $try"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
sleep 5
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Get Crowdin download URL
|
|
||||||
id: crowdin-download-url
|
|
||||||
env:
|
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
|
||||||
CROWDIN_BUILD_ID: ${{ steps.crowdin-build.outputs.id }}
|
|
||||||
run: |
|
|
||||||
# Step 4: when build is finished, get download url
|
|
||||||
DOWNLOAD_URL=$(
|
|
||||||
curl -s -H "Authorization: Bearer $CROWDIN_API_TOKEN" \
|
|
||||||
$CROWDIN_BASE_URL/$CROWDIN_PROJECT_ID/translations/builds/$CROWDIN_BUILD_ID/download | jq -r '.data.url'
|
|
||||||
)
|
|
||||||
echo "[*] Crowin translations download url: $DOWNLOAD_URL"
|
|
||||||
echo "::set-output name=value::$DOWNLOAD_URL"
|
|
||||||
|
|
||||||
- name: Download Crowdin translations
|
|
||||||
env:
|
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
|
||||||
CROWDIN_DOWNLOAD_URL: ${{ steps.crowdin-download-url.outputs.value }}
|
|
||||||
SAVE_FILE: "translations.zip"
|
|
||||||
run: |
|
|
||||||
# Step 5: download the translations via the download url
|
|
||||||
curl -s $CROWDIN_DOWNLOAD_URL --output $SAVE_FILE
|
|
||||||
echo "[*] Saved to: $SAVE_FILE"
|
|
||||||
unzip -o $SAVE_FILE
|
|
||||||
rm $SAVE_FILE
|
|
||||||
|
|
||||||
- name: Check changes
|
|
||||||
id: files-changed
|
|
||||||
env:
|
|
||||||
BRANCH_NAME: ${{ steps.branch.outputs.branch-name }}
|
|
||||||
BRANCH_EXISTS: ${{ steps.branch.outputs.branch-exists }}
|
|
||||||
run: |
|
|
||||||
DIFF_BRANCH=master
|
|
||||||
if [[ "$BRANCH_EXISTS" == "true" ]]; then
|
|
||||||
DIFF_BRANCH=$BRANCH_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
DIFF_LEN=$(git diff origin/${DIFF_BRANCH} | wc -l | xargs)
|
|
||||||
echo "[*] git diff lines: ${DIFF_LEN}"
|
|
||||||
echo "::set-output name=num::$DIFF_LEN"
|
|
||||||
|
|
||||||
- name: Commit changes
|
|
||||||
env:
|
|
||||||
BRANCH_NAME: ${{ steps.branch.outputs.branch-name }}
|
|
||||||
BRANCH_EXISTS: ${{ steps.branch.outputs.branch-exists }}
|
|
||||||
DIFF_BRANCH: master
|
|
||||||
DIFF_LEN: ${{ steps.files-changed.outputs.num }}
|
|
||||||
run: |
|
|
||||||
echo "=====Translations Changed====="
|
|
||||||
git diff --name-only origin/${DIFF_BRANCH}
|
|
||||||
echo "=============================="
|
|
||||||
|
|
||||||
if [ "$DIFF_LEN" != "0" ]; then
|
|
||||||
echo "[*] Adding new translations"
|
|
||||||
git add .
|
|
||||||
echo "[*] Committing"
|
|
||||||
git commit -m "Autosync Crowdin translations"
|
|
||||||
echo "[*] Pushing"
|
|
||||||
if [ "$BRANCH_EXISTS" == "false" ]; then
|
|
||||||
git push -u origin $BRANCH_NAME
|
|
||||||
else
|
|
||||||
git push
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "[*] No new docs"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create/Update PR
|
|
||||||
if: ${{ steps.files-changed.outputs.num }} != 0
|
|
||||||
env:
|
|
||||||
BRANCH_NAME: ${{ steps.branch.outputs.branch-name }}
|
|
||||||
BRANCH_EXISTS: ${{ steps.branch.outputs.branch-exists }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
if [ "$BRANCH_EXISTS" == "false" ]; then
|
|
||||||
echo "[*] Creating PR"
|
|
||||||
gh pr create --title "Autosync Crowdin Translations" \
|
|
||||||
--body "Autosync the updated translations"
|
|
||||||
else
|
|
||||||
echo "[*] Existing PR updated"
|
|
||||||
fi
|
|
||||||
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
cloc:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Set up cloc
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -y install cloc
|
|
||||||
|
|
||||||
- name: Print lines of code
|
|
||||||
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
|
|
||||||
@@ -46,8 +46,6 @@ 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"),
|
||||||
@@ -109,7 +107,6 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("org.torproject.torbrowser_alpha", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0a8)
|
new Browser("org.torproject.torbrowser_alpha", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0a8)
|
||||||
new Browser("org.ungoogled.chromium.extensions.stable", "url_bar"),
|
new Browser("org.ungoogled.chromium.extensions.stable", "url_bar"),
|
||||||
new Browser("org.ungoogled.chromium.stable", "url_bar"),
|
new Browser("org.ungoogled.chromium.stable", "url_bar"),
|
||||||
new Browser("us.spotco.fennec_dos", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
|
||||||
|
|
||||||
// [Section B] Entries only present here
|
// [Section B] Entries only present here
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
<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>
|
||||||
@@ -74,26 +75,24 @@
|
|||||||
<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.10</Version>
|
<Version>1.8.6.7</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.5.0" />
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.3-beta01" />
|
||||||
<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.7.0</Version>
|
<Version>1.5.3.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>122.0.0</Version>
|
<Version>121.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.3.0.1" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
|
||||||
<PackageReference Include="Xamarin.Google.Dagger" Version="2.37.0" />
|
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.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.Google.Dagger" Version="2.27.0" />
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||||
<Version>117.0.0</Version>
|
<Version>71.1600.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -119,18 +118,20 @@
|
|||||||
<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\ExtendedGridRenderer.cs" />
|
<Compile Include="Renderers\CipherViewCellRenderer.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" />
|
||||||
@@ -172,9 +173,6 @@
|
|||||||
<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" />
|
||||||
@@ -187,6 +185,7 @@
|
|||||||
<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,6 +262,12 @@
|
|||||||
<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,8 +63,6 @@ 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",
|
||||||
@@ -122,7 +120,6 @@ namespace Bit.Droid.Autofill
|
|||||||
"org.torproject.torbrowser_alpha",
|
"org.torproject.torbrowser_alpha",
|
||||||
"org.ungoogled.chromium.extensions.stable",
|
"org.ungoogled.chromium.extensions.stable",
|
||||||
"org.ungoogled.chromium.stable",
|
"org.ungoogled.chromium.stable",
|
||||||
"us.spotco.fennec_dos",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The URLs are blacklisted from autofilling
|
// The URLs are blacklisted from autofilling
|
||||||
@@ -143,14 +140,13 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
var allCiphers = ciphers.Item1.ToList();
|
var allCiphers = ciphers.Item1.ToList();
|
||||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||||
var nonPromptCiphers = allCiphers.Where(cipher => cipher.Reprompt == CipherRepromptType.None);
|
return allCiphers.Select(c => new FilledItem(c)).ToList();
|
||||||
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 && c.Reprompt == CipherRepromptType.None).Select(c => new FilledItem(c)).ToList();
|
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
|
||||||
}
|
}
|
||||||
return new List<FilledItem>();
|
return new List<FilledItem>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ 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,7 +27,6 @@ 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)]
|
||||||
@@ -51,6 +50,7 @@ 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;
|
||||||
@@ -63,6 +63,9 @@ 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);
|
||||||
@@ -100,7 +103,20 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "startEventTimer")
|
if (message.Command == "scheduleVaultTimeoutTimer")
|
||||||
|
{
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
@@ -149,48 +165,39 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this)
|
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
|
||||||
.GetAwaiter()
|
|
||||||
.GetResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
{
|
{
|
||||||
base.OnNewIntent(intent);
|
base.OnNewIntent(intent);
|
||||||
try
|
if (intent.GetBooleanExtra("generatorTile", false))
|
||||||
{
|
{
|
||||||
if (intent.GetBooleanExtra("generatorTile", false))
|
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||||
|
if (_appOptions != null)
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
_appOptions.GeneratorTile = true;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
else if (intent.GetBooleanExtra("myVaultTile", false))
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,11 +343,6 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
if (intent.Action == Intent.ActionSend && intent.Type != null)
|
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;
|
var type = intent.Type;
|
||||||
if (type.Contains("text/"))
|
if (type.Contains("text/"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,9 +99,6 @@ 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);
|
||||||
@@ -113,9 +110,6 @@ 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.12.0"
|
android:versionName="2.10.0"
|
||||||
android:installLocation="internalOnly"
|
android:installLocation="internalOnly"
|
||||||
package="com.x8bit.bitwarden">
|
package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
@@ -54,7 +54,12 @@
|
|||||||
<!-- Package visibility (for Android 11+) -->
|
<!-- Package visibility (for Android 11+) -->
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="*"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<data android:scheme="http"/>
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<data android:scheme="https"/>
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
|
|||||||
16
src/Android/Receivers/LockAlarmReceiver.cs
Normal file
16
src/Android/Receivers/LockAlarmReceiver.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
242
src/Android/Renderers/CipherViewCellRenderer.cs
Normal file
242
src/Android/Renderers/CipherViewCellRenderer.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
29
src/Android/Renderers/ExtendedListViewRenderer.cs
Normal file
29
src/Android/Renderers/ExtendedListViewRenderer.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
210
src/Android/Renderers/SendViewCellRenderer.cs
Normal file
210
src/Android/Renderers/SendViewCellRenderer.cs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
86
src/Android/Resources/layout/CipherViewCell.axml
Normal file
86
src/Android/Resources/layout/CipherViewCell.axml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?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>
|
||||||
104
src/Android/Resources/layout/SendViewCell.axml
Normal file
104
src/Android/Resources/layout/SendViewCell.axml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?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,7 +6,6 @@
|
|||||||
<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,12 +56,6 @@
|
|||||||
<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"/>
|
||||||
@@ -233,7 +227,4 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.ungoogled.chromium.stable"
|
android:name="org.ungoogled.chromium.stable"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
|
||||||
android:name="us.spotco.fennec_dos"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
</autofill-service>
|
</autofill-service>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Javax.Crypto;
|
||||||
|
using Javax.Crypto.Spec;
|
||||||
using Org.BouncyCastle.Crypto;
|
using Org.BouncyCastle.Crypto;
|
||||||
using Org.BouncyCastle.Crypto.Digests;
|
using Org.BouncyCastle.Crypto.Digests;
|
||||||
using Org.BouncyCastle.Crypto.Generators;
|
using Org.BouncyCastle.Crypto.Generators;
|
||||||
@@ -33,5 +35,25 @@ namespace Bit.Droid.Services
|
|||||||
generator.Init(password, salt, iterations);
|
generator.Init(password, salt, iterations);
|
||||||
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey();
|
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] AesGcmEncrypt(byte[] data, byte[] iv, byte[] key)
|
||||||
|
{
|
||||||
|
var secKey = new SecretKeySpec(key, "AES");
|
||||||
|
var cipher = Cipher.GetInstance("AES/GCM/NoPadding");
|
||||||
|
var gcmSpec = new GCMParameterSpec(128, iv);
|
||||||
|
cipher.Init(CipherMode.EncryptMode, secKey, gcmSpec);
|
||||||
|
var result = cipher.DoFinal(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] AesGcmDecrypt(byte[] data, byte[] iv, byte[] key)
|
||||||
|
{
|
||||||
|
var secKey = new SecretKeySpec(key, "AES");
|
||||||
|
var cipher = Cipher.GetInstance("AES/GCM/NoPadding");
|
||||||
|
var gcmSpec = new GCMParameterSpec(128, iv);
|
||||||
|
cipher.Init(CipherMode.DecryptMode, secKey, gcmSpec);
|
||||||
|
var result = cipher.DoFinal(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 password = false)
|
bool numericKeyboard = false, bool autofocus = true)
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
if (activity == null)
|
if (activity == null)
|
||||||
@@ -333,10 +333,6 @@ 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;
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ using Android.Content.PM;
|
|||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
[Activity(
|
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
|
||||||
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")]
|
||||||
|
|||||||
@@ -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 password = false);
|
bool autofocus = true);
|
||||||
void RateApp();
|
void RateApp();
|
||||||
bool SupportsFaceBiometric();
|
bool SupportsFaceBiometric();
|
||||||
Task<bool> SupportsFaceBiometricAsync();
|
Task<bool> SupportsFaceBiometricAsync();
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
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="4.3.0" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="3.2.1" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.2" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" />
|
||||||
<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="5.0.0.2083" />
|
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
|
||||||
<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>
|
||||||
@@ -411,4 +411,4 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -178,10 +178,6 @@ namespace Bit.App
|
|||||||
SyncIfNeeded();
|
SyncIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
|
||||||
}
|
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +216,7 @@ 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();
|
||||||
@@ -316,7 +313,11 @@ 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,111 +1,114 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
|
<ViewCell 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.Resources>
|
<Grid
|
||||||
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
x:Name="_grid"
|
||||||
<u:IconImageConverter x:Key="iconImageConverter"/>
|
StyleClass="list-row, list-row-platform"
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
RowSpacing="0"
|
||||||
</Grid.Resources>
|
ColumnSpacing="0"
|
||||||
|
x:DataType="controls:CipherViewCellViewModel">
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.BindingContext>
|
||||||
<RowDefinition Height="Auto" />
|
<controls:CipherViewCellViewModel />
|
||||||
</Grid.RowDefinitions>
|
</Grid.BindingContext>
|
||||||
|
|
||||||
<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="Auto" />
|
<ColumnDefinition Width="40" />
|
||||||
<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
|
||||||
Grid.Column="1"
|
x:Name="_icon"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
HorizontalOptions="Start"
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-title-icon"
|
StyleClass="list-icon, list-icon-platform"
|
||||||
Margin="5, 0, 0, 0"
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
|
<ff:CachedImage
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
x:Name="_image"
|
||||||
AutomationProperties.Name="{u:I18n Shared}" />
|
Grid.Row="0"
|
||||||
<controls:FaLabel
|
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"
|
Grid.Column="2"
|
||||||
Grid.Row="0"
|
Text=""
|
||||||
HorizontalOptions="Start"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
VerticalOptions="Center"
|
Clicked="MoreButton_Clicked"
|
||||||
StyleClass="list-title-icon"
|
VerticalOptions="CenterAndExpand"
|
||||||
Margin="5, 0, 0, 0"
|
HorizontalOptions="EndAndExpand"
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Attachments}" />
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<controls:MiButton
|
</ViewCell>
|
||||||
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,26 +1,45 @@
|
|||||||
using System;
|
using Bit.App.Pages;
|
||||||
|
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 : ExtendedGrid
|
public partial class CipherViewCell : ViewCell
|
||||||
{
|
{
|
||||||
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));
|
nameof(WebsiteIconsEnabled), typeof(bool), typeof(CipherViewCell), true, BindingMode.OneWay);
|
||||||
|
|
||||||
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()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
|
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);
|
||||||
@@ -41,31 +60,130 @@ 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)
|
||||||
{
|
{
|
||||||
if (Cipher == null)
|
_viewModel.Cipher = Cipher;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BindingContext = new CipherViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
|
|
||||||
}
|
}
|
||||||
else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
|
}
|
||||||
|
|
||||||
|
protected override void OnBindingContextChanged()
|
||||||
|
{
|
||||||
|
base.OnBindingContextChanged();
|
||||||
|
if (_usingNativeCell)
|
||||||
{
|
{
|
||||||
if (Cipher == null)
|
return;
|
||||||
{
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
var cipher = ((sender as MiButton)?.BindingContext as CipherViewCellViewModel)?.Cipher;
|
ButtonCommand?.Execute(Cipher);
|
||||||
if (cipher != null)
|
|
||||||
{
|
|
||||||
ButtonCommand?.Execute(cipher);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,11 @@ 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public class ExtendedCollectionView : CollectionView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public class ExtendedGrid : Grid
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
src/App/Controls/ExtendedListView.cs
Normal file
12
src/App/Controls/ExtendedListView.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedListView : ListView
|
||||||
|
{
|
||||||
|
public ExtendedListView() { }
|
||||||
|
|
||||||
|
public ExtendedListView(ListViewCachingStrategy cachingStrategy)
|
||||||
|
: base(cachingStrategy) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public class ExtendedStackLayout : StackLayout
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +1,137 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
|
<ViewCell 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.Resources>
|
<Grid
|
||||||
<u:SendIconGlyphConverter x:Key="sendIconGlyphConverter"/>
|
x:Name="_grid"
|
||||||
</Grid.Resources>
|
StyleClass="list-row, list-row-platform"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0"
|
||||||
|
x:DataType="controls:SendViewCellViewModel">
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.BindingContext>
|
||||||
<RowDefinition Height="Auto" />
|
<controls:SendViewCellViewModel />
|
||||||
</Grid.RowDefinitions>
|
</Grid.BindingContext>
|
||||||
|
|
||||||
<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="Auto" />
|
<ColumnDefinition Width="40" />
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<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 Send.Name}" />
|
|
||||||
<Label
|
|
||||||
LineBreakMode="TailTruncation"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="6"
|
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
|
||||||
Text="{Binding Send.DisplayDate}" />
|
|
||||||
<controls:FaLabel
|
<controls:FaLabel
|
||||||
Grid.Column="1"
|
x:Name="_icon"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
HorizontalOptions="Start"
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-title-icon"
|
StyleClass="list-icon, list-icon-platform"
|
||||||
Margin="5, 0, 0, 0"
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
Text=""
|
|
||||||
IsVisible="{Binding Send.Disabled, Mode=OneTime}"
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
<Grid.RowDefinitions>
|
||||||
AutomationProperties.Name="{u:I18n Disabled}" />
|
<RowDefinition Height="Auto" />
|
||||||
<controls:FaLabel
|
<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.Column="2"
|
Grid.Column="2"
|
||||||
Grid.Row="0"
|
Text=""
|
||||||
HorizontalOptions="Start"
|
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||||
VerticalOptions="Center"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
StyleClass="list-title-icon"
|
Clicked="MoreButton_Clicked"
|
||||||
Margin="5, 0, 0, 0"
|
VerticalOptions="CenterAndExpand"
|
||||||
Text=""
|
HorizontalOptions="EndAndExpand"
|
||||||
IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Password}" />
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
<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>
|
||||||
|
|
||||||
<controls:MiButton
|
</ViewCell>
|
||||||
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,10 +1,14 @@
|
|||||||
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 : ExtendedGrid
|
public partial class SendViewCell : ViewCell
|
||||||
{
|
{
|
||||||
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);
|
||||||
@@ -13,11 +17,25 @@ 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), true, BindingMode.OneWay);
|
nameof(ShowOptions), typeof(bool), typeof(SendViewCell));
|
||||||
|
|
||||||
|
private readonly IEnvironmentService _environmentService;
|
||||||
|
|
||||||
|
private SendViewCellViewModel _viewModel;
|
||||||
|
private bool _usingNativeCell;
|
||||||
|
|
||||||
public SendViewCell()
|
public SendViewCell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_viewModel = _grid.BindingContext as SendViewCellViewModel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_usingNativeCell = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendView Send
|
public SendView Send
|
||||||
@@ -34,30 +52,72 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public bool ShowOptions
|
public bool ShowOptions
|
||||||
{
|
{
|
||||||
get => (bool)GetValue(ShowOptionsProperty);
|
get => GetValue(ShowOptionsProperty) is bool && (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)
|
||||||
{
|
{
|
||||||
if (Send == null)
|
_viewModel.Send = Send;
|
||||||
{
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
var send = ((sender as MiButton)?.BindingContext as SendViewCellViewModel)?.Send;
|
ButtonCommand?.Execute(Send);
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
ButtonCommand?.Execute(send);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ 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;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ 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;
|
||||||
|
|
||||||
@@ -38,6 +37,7 @@ 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()
|
||||||
@@ -208,7 +208,6 @@ namespace Bit.App.Pages
|
|||||||
if (!failed)
|
if (!failed)
|
||||||
{
|
{
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +217,6 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,8 +226,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
_invalidPinAttempts++;
|
||||||
if (invalidUnlockAttempts >= 5)
|
if (_invalidPinAttempts >= 5)
|
||||||
{
|
{
|
||||||
_messagingService.Send("logout");
|
_messagingService.Send("logout");
|
||||||
return;
|
return;
|
||||||
@@ -241,31 +239,32 @@ 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 storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
var passwordValid = false;
|
var passwordValid = false;
|
||||||
|
if (keyHash != null)
|
||||||
if (storedKeyHash != null)
|
|
||||||
{
|
{
|
||||||
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
}
|
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
|
|
||||||
{
|
{
|
||||||
await _apiService.PostAccountVerifyPasswordAsync(request);
|
passwordValid = storedKeyHash == keyHash;
|
||||||
passwordValid = true;
|
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
else
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -279,7 +278,6 @@ namespace Bit.App.Pages
|
|||||||
_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
|
||||||
@@ -290,12 +288,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Button Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" IsEnabled="{Binding LoginEnabled}"/>
|
<Button Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -6,17 +6,11 @@ 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;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Text;
|
|
||||||
using Xamarin.Essentials;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class LoginPageViewModel : CaptchaProtectedViewModel
|
public class LoginPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
private const string Keys_RememberedEmail = "rememberedEmail";
|
||||||
private const string Keys_RememberEmail = "rememberEmail";
|
private const string Keys_RememberEmail = "rememberEmail";
|
||||||
@@ -27,13 +21,10 @@ namespace Bit.App.Pages
|
|||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
|
||||||
private readonly II18nService _i18nService;
|
|
||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _email;
|
private string _email;
|
||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
private bool _loginEnabled = true;
|
|
||||||
|
|
||||||
public LoginPageViewModel()
|
public LoginPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -43,8 +34,6 @@ namespace Bit.App.Pages
|
|||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -73,16 +62,6 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _masterPassword, value);
|
set => SetProperty(ref _masterPassword, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoginEnabled {
|
|
||||||
get => _loginEnabled;
|
|
||||||
set => SetProperty(ref _loginEnabled, value);
|
|
||||||
}
|
|
||||||
public bool Loading
|
|
||||||
{
|
|
||||||
get => !LoginEnabled;
|
|
||||||
set => LoginEnabled = !value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
@@ -90,12 +69,7 @@ namespace Bit.App.Pages
|
|||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action LogInSuccessAction { get; set; }
|
public Action LogInSuccessAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
protected override II18nService i18nService => _i18nService;
|
|
||||||
protected override IEnvironmentService environmentService => _environmentService;
|
|
||||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
|
||||||
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
@@ -140,13 +114,9 @@ namespace Bit.App.Pages
|
|||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!Loading)
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
{
|
var response = await _authService.LogInAsync(Email, MasterPassword);
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
MasterPassword = string.Empty;
|
||||||
Loading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
|
||||||
if (RememberEmail)
|
if (RememberEmail)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
||||||
@@ -155,26 +125,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.CaptchaNeeded)
|
|
||||||
{
|
|
||||||
if (await HandleCaptchaAsync(response.CaptchaSiteKey))
|
|
||||||
{
|
|
||||||
await LogInAsync();
|
|
||||||
_captchaToken = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Loading = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MasterPassword = string.Empty;
|
|
||||||
_captchaToken = null;
|
|
||||||
|
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
{
|
{
|
||||||
StartTwoFactorAction?.Invoke();
|
StartTwoFactorAction?.Invoke();
|
||||||
@@ -189,8 +140,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
_captchaToken = null;
|
|
||||||
MasterPassword = string.Empty;
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
@@ -198,7 +147,6 @@ namespace Bit.App.Pages
|
|||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" 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>
|
||||||
@@ -35,10 +36,6 @@
|
|||||||
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,7 +5,6 @@ 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;
|
||||||
@@ -183,7 +182,6 @@ 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);
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" IsEnabled="{Binding SubmitEnabled}"/>
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
|||||||
@@ -12,17 +12,14 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class RegisterPageViewModel : CaptchaProtectedViewModel
|
public class RegisterPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly II18nService _i18nService;
|
|
||||||
private readonly IEnvironmentService _environmentService;
|
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _acceptPolicies;
|
private bool _acceptPolicies;
|
||||||
private bool _submitEnabled = true;
|
|
||||||
|
|
||||||
public RegisterPageViewModel()
|
public RegisterPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -30,8 +27,6 @@ namespace Bit.App.Pages
|
|||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
|
||||||
|
|
||||||
PageTitle = AppResources.CreateAccount;
|
PageTitle = AppResources.CreateAccount;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -60,16 +55,6 @@ namespace Bit.App.Pages
|
|||||||
get => _acceptPolicies;
|
get => _acceptPolicies;
|
||||||
set => SetProperty(ref _acceptPolicies, value);
|
set => SetProperty(ref _acceptPolicies, value);
|
||||||
}
|
}
|
||||||
public bool SubmitEnabled
|
|
||||||
{
|
|
||||||
get => _submitEnabled;
|
|
||||||
set => SetProperty(ref _submitEnabled, value);
|
|
||||||
}
|
|
||||||
public bool Loading
|
|
||||||
{
|
|
||||||
get => !SubmitEnabled;
|
|
||||||
set => SubmitEnabled = !value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Thickness SwitchMargin
|
public Thickness SwitchMargin
|
||||||
{
|
{
|
||||||
@@ -91,11 +76,6 @@ namespace Bit.App.Pages
|
|||||||
public Action RegistrationSuccess { get; set; }
|
public Action RegistrationSuccess { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
protected override II18nService i18nService => _i18nService;
|
|
||||||
protected override IEnvironmentService environmentService => _environmentService;
|
|
||||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
|
||||||
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
@@ -165,21 +145,15 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
PublicKey = keys.Item1,
|
PublicKey = keys.Item1,
|
||||||
EncryptedPrivateKey = keys.Item2.EncryptedString
|
EncryptedPrivateKey = keys.Item2.EncryptedString
|
||||||
},
|
}
|
||||||
CaptchaResponse = _captchaToken,
|
|
||||||
};
|
};
|
||||||
// TODO: org invite?
|
// TODO: org invite?
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!Loading)
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
{
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
|
||||||
Loading = true;
|
|
||||||
}
|
|
||||||
await _apiService.PostRegisterAsync(request);
|
await _apiService.PostRegisterAsync(request);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
Loading = false;
|
|
||||||
_platformUtilsService.ShowToast("success", null, AppResources.AccountCreated,
|
_platformUtilsService.ShowToast("success", null, AppResources.AccountCreated,
|
||||||
new System.Collections.Generic.Dictionary<string, object>
|
new System.Collections.Generic.Dictionary<string, object>
|
||||||
{
|
{
|
||||||
@@ -189,23 +163,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
if (e?.Error != null && e.Error.CaptchaRequired)
|
|
||||||
{
|
|
||||||
if (await HandleCaptchaAsync(e.Error.CaptchaSiteKey))
|
|
||||||
{
|
|
||||||
await SubmitAsync();
|
|
||||||
_captchaToken = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
|
||||||
Loading = false;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
Loading = false;
|
|
||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
|
|||||||
@@ -138,8 +138,7 @@ 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, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
|
||||||
|
|
||||||
Tuple<SymmetricCryptoKey, EncString> encKey;
|
Tuple<SymmetricCryptoKey, EncString> encKey;
|
||||||
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
var existingEncKey = await _cryptoService.GetEncKeyAsync();
|
||||||
@@ -175,7 +174,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(localMasterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(masterPasswordHash);
|
||||||
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,21 +16,14 @@
|
|||||||
<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>
|
||||||
|
|
||||||
@@ -52,8 +45,7 @@
|
|||||||
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
|
||||||
@@ -131,12 +123,6 @@
|
|||||||
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"
|
||||||
@@ -145,6 +131,9 @@
|
|||||||
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,6 +1,5 @@
|
|||||||
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;
|
||||||
@@ -46,17 +45,26 @@ 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();
|
||||||
@@ -137,21 +145,6 @@ 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())
|
||||||
@@ -202,10 +195,5 @@ 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,8 +32,6 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -75,18 +73,6 @@ 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;
|
||||||
|
|
||||||
@@ -183,7 +169,14 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
}
|
}
|
||||||
ShowContinue = !(SelectedProviderType == null || DuoMethod);
|
if (SelectedProviderType == null || DuoMethod)
|
||||||
|
{
|
||||||
|
page.RemoveContinueButton();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
page.AddContinueButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
@@ -252,7 +245,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 && method != null)
|
else if (method != AppResources.Cancel)
|
||||||
{
|
{
|
||||||
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
||||||
if (selected == SelectedProviderType)
|
if (selected == SelectedProviderType)
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Xamarin.Essentials;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
|
||||||
{
|
|
||||||
public abstract class CaptchaProtectedViewModel : BaseViewModel
|
|
||||||
{
|
|
||||||
protected abstract II18nService i18nService { get; }
|
|
||||||
protected abstract IEnvironmentService environmentService { get; }
|
|
||||||
protected abstract IDeviceActionService deviceActionService { get; }
|
|
||||||
protected abstract IPlatformUtilsService platformUtilsService { get; }
|
|
||||||
protected string _captchaToken = null;
|
|
||||||
|
|
||||||
protected async Task<bool> HandleCaptchaAsync(string CaptchaSiteKey)
|
|
||||||
{
|
|
||||||
var callbackUri = "bitwarden://captcha-callback";
|
|
||||||
var data = EncodeDataParameter(new
|
|
||||||
{
|
|
||||||
siteKey = CaptchaSiteKey,
|
|
||||||
locale = i18nService.Culture.TwoLetterISOLanguageName,
|
|
||||||
callbackUri = callbackUri,
|
|
||||||
captchaRequiredText = AppResources.CaptchaRequired,
|
|
||||||
});
|
|
||||||
|
|
||||||
var url = environmentService.GetWebVaultUrl() + "/captcha-mobile-connector.html?" + "data=" + data +
|
|
||||||
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=1";
|
|
||||||
|
|
||||||
WebAuthenticatorResult authResult = null;
|
|
||||||
bool cancelled = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url),
|
|
||||||
new Uri(callbackUri));
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
await deviceActionService.HideLoadingAsync();
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// WebAuthenticator throws NSErrorException if iOS flow is cancelled - by setting cancelled to true
|
|
||||||
// here we maintain the appearance of a clean cancellation (we don't want to do this across the board
|
|
||||||
// because we still want to present legitimate errors). If/when this is fixed, we can remove this
|
|
||||||
// particular catch block (catching taskCanceledException above must remain)
|
|
||||||
// https://github.com/xamarin/Essentials/issues/1240
|
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
await deviceActionService.HideLoadingAsync();
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancelled == false && authResult != null &&
|
|
||||||
authResult.Properties.TryGetValue("token", out _captchaToken))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await platformUtilsService.ShowDialogAsync(AppResources.CaptchaFailed,
|
|
||||||
AppResources.CaptchaRequired);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EncodeDataParameter(object obj)
|
|
||||||
{
|
|
||||||
string EncodeMultibyte(Match match)
|
|
||||||
{
|
|
||||||
return Convert.ToChar(Convert.ToUInt32($"0x{match.Groups[1].Value}", 16)).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
var escaped = Uri.EscapeDataString(JsonConvert.SerializeObject(obj));
|
|
||||||
var multiByteEscaped = Regex.Replace(escaped, "%([0-9A-F]{2})", EncodeMultibyte);
|
|
||||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(multiByteEscaped));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,53 +43,57 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<controls:ExtendedCollectionView
|
<ListView x:Name="_listView"
|
||||||
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">
|
||||||
<CollectionView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||||
<Grid
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform"
|
<Grid
|
||||||
Padding="10"
|
StyleClass="list-row, list-row-platform"
|
||||||
RowSpacing="0"
|
Padding="10"
|
||||||
ColumnSpacing="10">
|
RowSpacing="0"
|
||||||
|
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>
|
||||||
</CollectionView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Name="_closeItem" x:Key="closeItem" />
|
x:Name="_closeItem" x:Key="closeItem" />
|
||||||
<ToolbarItem Text="{u:I18n Select}"
|
<ToolbarItem Text="{u:I18n Select}"
|
||||||
Clicked="Select_Clicked"
|
Clicked="Select_Clicked"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<u:IsNullConverter x:Key="null" />
|
<u:IsNullConverter x:Key="null" />
|
||||||
<u:IsNotNullConverter x:Key="notNull" />
|
<u:IsNotNullConverter x:Key="notNull" />
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Key="closeItem" x:Name="_closeItem" />
|
x:Key="closeItem" x:Name="_closeItem" />
|
||||||
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary" x:Name="_moreItem"
|
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary" x:Name="_moreItem"
|
||||||
x:Key="moreItem"
|
x:Key="moreItem"
|
||||||
@@ -294,23 +294,24 @@
|
|||||||
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="False" />
|
IsVisible="{Binding ShowOptions}" />
|
||||||
<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="False" />
|
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout IsVisible="True">
|
<StackLayout IsVisible="{Binding ShowOptions}">
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
Margin="0,10,0,0">
|
Margin="0,10,0,0">
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models;
|
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;
|
||||||
@@ -17,7 +16,6 @@ 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 AppOptions _appOptions;
|
||||||
private SendAddEditPageViewModel _vm;
|
private SendAddEditPageViewModel _vm;
|
||||||
@@ -28,7 +26,6 @@ namespace Bit.App.Pages
|
|||||||
SendType? type = null)
|
SendType? type = null)
|
||||||
{
|
{
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as SendAddEditPageViewModel;
|
_vm = BindingContext as SendAddEditPageViewModel;
|
||||||
@@ -82,14 +79,6 @@ 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 =>
|
||||||
{
|
{
|
||||||
@@ -120,15 +109,6 @@ 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();
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace Bit.App.Pages
|
|||||||
};
|
};
|
||||||
private bool _disableHideEmail;
|
private bool _disableHideEmail;
|
||||||
private bool _sendOptionsPolicyInEffect;
|
private bool _sendOptionsPolicyInEffect;
|
||||||
|
private bool _disableHideEmailControl;
|
||||||
|
|
||||||
public SendAddEditPageViewModel()
|
public SendAddEditPageViewModel()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,32 +44,34 @@
|
|||||||
<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 BindingContext.SendEnabled, Source={x:Reference _page}}" />
|
ShowOptions="{Binding ShowOptions}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="sendGroupTemplate"
|
<DataTemplate x:Key="sendGroupTemplate"
|
||||||
x:DataType="pages:SendGroupingsPageListItem">
|
x:DataType="pages:SendGroupingsPageListItem">
|
||||||
<controls:ExtendedStackLayout Orientation="Horizontal"
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform">
|
<StackLayout Orientation="Horizontal"
|
||||||
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
StyleClass="list-row, list-row-platform">
|
||||||
HorizontalOptions="Start"
|
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
VerticalOptions="Center"
|
HorizontalOptions="Start"
|
||||||
StyleClass="list-icon, list-icon-platform">
|
VerticalOptions="Center"
|
||||||
<controls:FaLabel.Effects>
|
StyleClass="list-icon, list-icon-platform">
|
||||||
<effects:FixedSizeEffect />
|
<controls:FaLabel.Effects>
|
||||||
</controls:FaLabel.Effects>
|
<effects:FixedSizeEffect />
|
||||||
</controls:FaLabel>
|
</controls:FaLabel.Effects>
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
</controls:FaLabel>
|
||||||
LineBreakMode="TailTruncation"
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
HorizontalOptions="FillAndExpand"
|
LineBreakMode="TailTruncation"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
StyleClass="list-title" />
|
VerticalOptions="CenterAndExpand"
|
||||||
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
StyleClass="list-title" />
|
||||||
HorizontalOptions="End"
|
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalOptions="End"
|
||||||
HorizontalTextAlignment="End"
|
VerticalOptions="CenterAndExpand"
|
||||||
StyleClass="list-sub" />
|
HorizontalTextAlignment="End"
|
||||||
</controls:ExtendedStackLayout>
|
StyleClass="list-sub" />
|
||||||
|
</StackLayout>
|
||||||
|
</ViewCell>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
||||||
@@ -101,25 +103,32 @@
|
|||||||
Text="{Binding NoDataText}"
|
Text="{Binding NoDataText}"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n AddASend}"
|
Text="{u:I18n AddAnItem}"
|
||||||
Clicked="AddButton_Clicked" />
|
Clicked="AddButton_Clicked"
|
||||||
|
IsVisible="{Binding ShowAddSendButton}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<RefreshView
|
<controls:ExtendedListView
|
||||||
|
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}"
|
||||||
Command="{Binding RefreshCommand}">
|
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||||
<controls:ExtendedCollectionView
|
IsGroupingEnabled="True"
|
||||||
ItemsSource="{Binding GroupedSends}"
|
ItemSelected="RowSelected"
|
||||||
VerticalOptions="FillAndExpand"
|
StyleClass="list, list-platform">
|
||||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
<x:Arguments>
|
||||||
IsGrouped="True"
|
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
||||||
SelectionMode="Single"
|
</x:Arguments>
|
||||||
SelectionChanged="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
|
||||||
|
|
||||||
<CollectionView.GroupHeaderTemplate>
|
<ListView.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">
|
||||||
@@ -137,10 +146,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>
|
||||||
</DataTemplate>
|
</ViewCell>
|
||||||
</CollectionView.GroupHeaderTemplate>
|
</DataTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView.GroupHeaderTemplate>
|
||||||
</RefreshView>
|
</controls:ExtendedListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -20,13 +20,14 @@ namespace Bit.App.Pages
|
|||||||
private readonly string _pageName;
|
private readonly string _pageName;
|
||||||
|
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
|
private PreviousPageInfo _previousPage;
|
||||||
|
|
||||||
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
|
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
|
||||||
AppOptions appOptions = null)
|
AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||||
{
|
{
|
||||||
_pageName = string.Concat(nameof(SendGroupingsPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SetActivityIndicator(_mainContent);
|
ListView = _listView;
|
||||||
_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");
|
||||||
@@ -36,6 +37,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.MainPage = mainPage;
|
_vm.MainPage = mainPage;
|
||||||
_vm.Type = type;
|
_vm.Type = type;
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
|
_previousPage = previousPage;
|
||||||
if (pageTitle != null)
|
if (pageTitle != null)
|
||||||
{
|
{
|
||||||
_vm.PageTitle = pageTitle;
|
_vm.PageTitle = pageTitle;
|
||||||
@@ -44,10 +46,7 @@ namespace Bit.App.Pages
|
|||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
_absLayout.Children.Remove(_fab);
|
_absLayout.Children.Remove(_fab);
|
||||||
if (type == null)
|
ToolbarItems.Add(_aboutIconItem);
|
||||||
{
|
|
||||||
ToolbarItems.Add(_aboutIconItem);
|
|
||||||
}
|
|
||||||
ToolbarItems.Add(_addItem);
|
ToolbarItems.Add(_addItem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -58,6 +57,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExtendedListView ListView { get; set; }
|
||||||
|
|
||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -135,14 +136,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.CurrentSelection?.FirstOrDefault() is SendGroupingsPageListItem item))
|
if (!(e.SelectedItem is SendGroupingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> sendGroupItems, string name, int count,
|
public SendGroupingsPageListGroup(List<SendGroupingsPageListItem> groupItems, string name, int count,
|
||||||
bool doUpper = true, bool first = false)
|
bool doUpper = true, bool first = false)
|
||||||
{
|
{
|
||||||
AddRange(sendGroupItems);
|
AddRange(groupItems);
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
Name = "-";
|
Name = "-";
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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;
|
||||||
@@ -90,6 +91,11 @@ 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;
|
||||||
|
|||||||
@@ -60,22 +60,23 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<controls:ExtendedCollectionView
|
<ListView x:Name="_listView"
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding Sends}"
|
ItemsSource="{Binding Sends}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
HasUnevenRows="true"
|
||||||
SelectionChanged="RowSelected"
|
CachingStrategy="RecycleElement"
|
||||||
|
ItemSelected="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<CollectionView.ItemTemplate>
|
<ListView.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>
|
||||||
</CollectionView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
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;
|
||||||
@@ -89,15 +87,15 @@ namespace Bit.App.Pages
|
|||||||
Navigation.PopModalAsync(false);
|
Navigation.PopModalAsync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.CurrentSelection?.FirstOrDefault() is SendView send)
|
if (e.SelectedItem is SendView send)
|
||||||
{
|
{
|
||||||
await _vm.SelectSendAsync(send);
|
await _vm.SelectSendAsync(send);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
|||||||
@@ -103,9 +103,11 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(_masterPassword, null);
|
var keyHash = await _cryptoService.HashPasswordAsync(_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;
|
||||||
@@ -126,7 +128,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.UTF8.GetBytes(data);
|
_exportResult = Encoding.ASCII.GetBytes(data);
|
||||||
|
|
||||||
if (!_deviceActionService.SaveFile(_exportResult, null, _defaultFilename, null))
|
if (!_deviceActionService.SaveFile(_exportResult, null, _defaultFilename, null))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" />
|
||||||
<ToolbarItem Text="{u:I18n Delete}"
|
<ToolbarItem Text="{u:I18n Delete}"
|
||||||
Clicked="Delete_Clicked"
|
Clicked="Delete_Clicked"
|
||||||
|
|||||||
@@ -32,25 +32,27 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<controls:ExtendedCollectionView
|
<ListView x:Name="_listView"
|
||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding Folders}"
|
ItemsSource="{Binding Folders}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
CachingStrategy="RecycleElement"
|
||||||
SelectionChanged="RowSelected"
|
ItemSelected="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<CollectionView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:FolderView">
|
<DataTemplate x:DataType="views:FolderView">
|
||||||
<controls:ExtendedStackLayout
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform"
|
<StackLayout
|
||||||
Padding="10">
|
StyleClass="list-row, list-row-platform"
|
||||||
<Label LineBreakMode="TailTruncation"
|
Padding="10">
|
||||||
StyleClass="list-title, list-title-platform"
|
<Label LineBreakMode="TailTruncation"
|
||||||
Text="{Binding Name, Mode=OneWay}" />
|
StyleClass="list-title, list-title-platform"
|
||||||
</controls:ExtendedStackLayout>
|
Text="{Binding Name, Mode=OneWay}" />
|
||||||
|
</StackLayout>
|
||||||
|
</ViewCell>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</CollectionView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
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
|
||||||
@@ -34,14 +32,14 @@ namespace Bit.App.Pages
|
|||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.CurrentSelection?.FirstOrDefault() is FolderView folder))
|
if (!(e.SelectedItem is FolderView folder))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,21 +19,23 @@
|
|||||||
<DataTemplate
|
<DataTemplate
|
||||||
x:Key="regularTemplate"
|
x:Key="regularTemplate"
|
||||||
x:DataType="pages:SettingsPageListItem">
|
x:DataType="pages:SettingsPageListItem">
|
||||||
<controls:ExtendedStackLayout Orientation="Horizontal"
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform">
|
<StackLayout Orientation="Horizontal"
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
StyleClass="list-row, list-row-platform">
|
||||||
LineBreakMode="TailTruncation"
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
HorizontalOptions="StartAndExpand"
|
LineBreakMode="TailTruncation"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalOptions="StartAndExpand"
|
||||||
StyleClass="list-title"/>
|
VerticalOptions="CenterAndExpand"
|
||||||
<Label Text="{Binding SubLabel, Mode=OneWay}"
|
StyleClass="list-title"/>
|
||||||
IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}"
|
<Label Text="{Binding SubLabel, Mode=OneWay}"
|
||||||
HorizontalOptions="End"
|
IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}"
|
||||||
HorizontalTextAlignment="End"
|
HorizontalOptions="End"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalTextAlignment="End"
|
||||||
TextColor="{Binding SubLabelColor}"
|
VerticalOptions="CenterAndExpand"
|
||||||
StyleClass="list-sub" />
|
TextColor="{Binding SubLabelColor}"
|
||||||
</controls:ExtendedStackLayout>
|
StyleClass="list-sub" />
|
||||||
|
</StackLayout>
|
||||||
|
</ViewCell>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SettingsPageListItemSelector
|
<pages:SettingsPageListItemSelector
|
||||||
@@ -42,32 +44,35 @@
|
|||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<controls:ExtendedCollectionView
|
<ListView
|
||||||
ItemsSource="{Binding GroupedItems}"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
|
HasUnevenRows="True"
|
||||||
|
RowHeight="-1"
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
IsGrouped="True"
|
IsGroupingEnabled="True"
|
||||||
SelectionMode="Single"
|
ItemSelected="RowSelected"
|
||||||
SelectionChanged="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
|
|
||||||
<CollectionView.GroupHeaderTemplate>
|
<ListView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:SettingsPageListGroup">
|
<DataTemplate x:DataType="pages:SettingsPageListGroup">
|
||||||
<StackLayout
|
<ViewCell>
|
||||||
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
<StackLayout
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
||||||
<BoxView
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
<BoxView
|
||||||
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
||||||
<Label
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
Text="{Binding Name}"
|
<Label
|
||||||
StyleClass="list-header, list-header-platform" />
|
Text="{Binding Name}"
|
||||||
|
StyleClass="list-header, list-header-platform" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
</ViewCell>
|
||||||
</StackLayout>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</CollectionView.GroupHeaderTemplate>
|
</ListView.GroupHeaderTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
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
|
||||||
@@ -43,14 +41,14 @@ namespace Bit.App.Pages
|
|||||||
return base.OnBackButtonPressed();
|
return base.OnBackButtonPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item))
|
if (!(e.SelectedItem is SettingsPageListItem item))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -103,7 +101,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage()));
|
await Navigation.PushModalAsync(new NavigationPage(new ExportVaultPage()));
|
||||||
}
|
}
|
||||||
else if (item.Name == AppResources.LearnOrg)
|
else if (item.Name == AppResources.ShareVault)
|
||||||
{
|
{
|
||||||
await _vm.ShareAsync();
|
await _vm.ShareAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task ShareAsync()
|
public async Task ShareAsync()
|
||||||
{
|
{
|
||||||
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LearnOrgConfirmation,
|
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.ShareVaultConfirmation,
|
||||||
AppResources.LearnOrg, AppResources.Yes, AppResources.Cancel);
|
AppResources.ShareVault, AppResources.Yes, AppResources.Cancel);
|
||||||
if (confirmed)
|
if (confirmed)
|
||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/what-is-an-organization/");
|
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/what-is-an-organization/");
|
||||||
@@ -379,23 +379,17 @@ 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.LearnOrg });
|
|
||||||
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 },
|
||||||
@@ -414,15 +408,6 @@ 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;
|
||||||
|
|||||||
@@ -23,14 +23,14 @@
|
|||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<u:StringHasValueConverter x:Key="stringHasValue" />
|
<u:StringHasValueConverter x:Key="stringHasValue" />
|
||||||
<u:IsNotNullConverter x:Key="notNull" />
|
<u:IsNotNullConverter x:Key="notNull" />
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Key="closeItem" x:Name="_closeItem" />
|
x:Key="closeItem" x:Name="_closeItem" />
|
||||||
<ToolbarItem Text="{u:I18n Collections}"
|
<ToolbarItem Text="{u:I18n Collections}"
|
||||||
x:Key="collectionsItem"
|
x:Key="collectionsItem"
|
||||||
x:Name="_collectionsItem"
|
x:Name="_collectionsItem"
|
||||||
Clicked="Collections_Clicked"
|
Clicked="Collections_Clicked"
|
||||||
Order="Secondary" />
|
Order="Secondary" />
|
||||||
<ToolbarItem Text="{u:I18n MoveToOrganization}"
|
<ToolbarItem Text="{u:I18n Share}"
|
||||||
x:Key="shareItem"
|
x:Key="shareItem"
|
||||||
x:Name="_shareItem"
|
x:Name="_shareItem"
|
||||||
Clicked="Share_Clicked"
|
Clicked="Share_Clicked"
|
||||||
@@ -219,40 +219,15 @@
|
|||||||
Text="{Binding Cipher.Card.CardholderName}"
|
Text="{Binding Cipher.Card.CardholderName}"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Grid StyleClass="box-row, box-row-input">
|
<StackLayout 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" />
|
||||||
Grid.Row="0"
|
<Entry
|
||||||
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" />
|
||||||
Grid.Row="1"
|
</StackLayout>
|
||||||
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}"
|
||||||
@@ -555,24 +530,6 @@
|
|||||||
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">
|
||||||
@@ -710,10 +667,7 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<controls:RepeaterView
|
<controls:RepeaterView ItemsSource="{Binding Collections}" IsVisible="{Binding HasCollections}">
|
||||||
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,7 +20,6 @@ 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;
|
||||||
@@ -39,7 +38,6 @@ 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;
|
||||||
@@ -49,7 +47,6 @@ 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;
|
||||||
@@ -147,14 +144,6 @@ 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);
|
||||||
@@ -169,7 +158,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
RequestFocus(_nameEntry);
|
RequestFocus(_nameEntry);
|
||||||
}
|
}
|
||||||
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +272,7 @@ namespace Bit.App.Pages
|
|||||||
var options = new List<string> { AppResources.Attachments };
|
var options = new List<string> { AppResources.Attachments };
|
||||||
if (_vm.EditMode)
|
if (_vm.EditMode)
|
||||||
{
|
{
|
||||||
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.MoveToOrganization : AppResources.Collections);
|
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.Share : AppResources.Collections);
|
||||||
}
|
}
|
||||||
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
||||||
(_vm.EditMode && !_vm.CloneMode) ? AppResources.Delete : null, options.ToArray());
|
(_vm.EditMode && !_vm.CloneMode) ? AppResources.Delete : null, options.ToArray());
|
||||||
@@ -305,7 +293,7 @@ namespace Bit.App.Pages
|
|||||||
var page = new CollectionsPage(_vm.CipherId);
|
var page = new CollectionsPage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.MoveToOrganization)
|
else if (selection == AppResources.Share)
|
||||||
{
|
{
|
||||||
var page = new SharePage(_vm.CipherId);
|
var page = new SharePage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
await Navigation.PushModalAsync(new Xamarin.Forms.NavigationPage(page));
|
||||||
@@ -388,10 +376,5 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PasswordPrompt_Toggled(object sender, ToggledEventArgs e)
|
|
||||||
{
|
|
||||||
_vm.Cipher.Reprompt = e.Value ? CipherRepromptType.Password : CipherRepromptType.None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Bit.App.Abstractions;
|
||||||
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;
|
||||||
@@ -10,7 +9,6 @@ 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;
|
||||||
|
|
||||||
@@ -31,7 +29,6 @@ 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;
|
||||||
@@ -41,7 +38,6 @@ 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[]
|
||||||
{
|
{
|
||||||
@@ -86,12 +82,10 @@ 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>();
|
||||||
@@ -147,12 +141,10 @@ 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; }
|
||||||
@@ -169,7 +161,6 @@ 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;
|
||||||
@@ -255,15 +246,6 @@ 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;
|
||||||
@@ -295,12 +277,10 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -711,16 +691,6 @@ 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;
|
||||||
@@ -747,11 +717,6 @@ 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)
|
||||||
@@ -804,30 +769,13 @@ 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,35 +47,40 @@
|
|||||||
Clicked="AddButton_Clicked"></Button>
|
Clicked="AddButton_Clicked"></Button>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<controls:ExtendedCollectionView
|
<controls:ExtendedListView
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding GroupedItems}"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
|
HasUnevenRows="true"
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
IsGrouped="True"
|
IsGroupingEnabled="True"
|
||||||
SelectionMode="Single"
|
ItemSelected="RowSelected"
|
||||||
SelectionChanged="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
|
<x:Arguments>
|
||||||
|
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
||||||
|
</x:Arguments>
|
||||||
|
|
||||||
<CollectionView.GroupHeaderTemplate>
|
<ListView.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
|
||||||
<StackLayout
|
<ViewCell>
|
||||||
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
<StackLayout
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
||||||
<BoxView
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
<BoxView
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
||||||
<Label
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
Text="{Binding Name}"
|
<Label
|
||||||
StyleClass="list-header, list-header-platform" />
|
Text="{Binding Name}"
|
||||||
<Label
|
StyleClass="list-header, list-header-platform" />
|
||||||
Text="{Binding ItemCount}"
|
<Label
|
||||||
StyleClass="list-header-sub" />
|
Text="{Binding ItemCount}"
|
||||||
|
StyleClass="list-header-sub" />
|
||||||
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</ViewCell>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</CollectionView.GroupHeaderTemplate>
|
</ListView.GroupHeaderTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</controls:ExtendedListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ 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
|
||||||
@@ -16,7 +13,6 @@ 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;
|
||||||
|
|
||||||
@@ -29,20 +25,11 @@ 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
|
||||||
@@ -57,23 +44,14 @@ namespace Bit.App.Pages
|
|||||||
}, _mainContent);
|
}, _mainContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnBackButtonPressed()
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
((ListView)sender).SelectedItem = null;
|
||||||
{
|
|
||||||
_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.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item && item.Cipher != null)
|
if (e.SelectedItem is GroupingsPageListItem item && item.Cipher != null)
|
||||||
{
|
{
|
||||||
await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill);
|
await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ 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;
|
||||||
@@ -36,7 +35,6 @@ 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);
|
||||||
@@ -120,14 +118,10 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (_deviceActionService.SystemMajorVersion() < 21)
|
if (_deviceActionService.SystemMajorVersion() < 21)
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
await AppHelpers.CipherListOptions(Page, cipher);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var autofillResponse = AppResources.Yes;
|
var autofillResponse = AppResources.Yes;
|
||||||
if (fuzzy)
|
if (fuzzy)
|
||||||
{
|
{
|
||||||
@@ -181,7 +175,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
await AppHelpers.CipherListOptions(Page, cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,14 +60,15 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
<controls:ExtendedCollectionView
|
<ListView x:Name="_listView"
|
||||||
IsVisible="{Binding ShowList}"
|
IsVisible="{Binding ShowList}"
|
||||||
ItemsSource="{Binding Ciphers}"
|
ItemsSource="{Binding Ciphers}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
HasUnevenRows="true"
|
||||||
SelectionChanged="RowSelected"
|
CachingStrategy="RecycleElement"
|
||||||
|
ItemSelected="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform">
|
||||||
<CollectionView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:CipherView">
|
<DataTemplate x:DataType="views:CipherView">
|
||||||
<controls:CipherViewCell
|
<controls:CipherViewCell
|
||||||
Cipher="{Binding .}"
|
Cipher="{Binding .}"
|
||||||
@@ -75,8 +76,8 @@
|
|||||||
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
|
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
|
||||||
/>
|
/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</CollectionView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ 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
|
||||||
@@ -121,15 +119,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.CurrentSelection?.FirstOrDefault() is CipherView cipher)
|
if (e.SelectedItem is CipherView cipher)
|
||||||
{
|
{
|
||||||
await _vm.SelectCipherAsync(cipher);
|
await _vm.SelectCipherAsync(cipher);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ 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;
|
||||||
@@ -36,7 +35,6 @@ 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);
|
||||||
@@ -184,7 +182,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (_deviceActionService.SystemMajorVersion() < 21)
|
if (_deviceActionService.SystemMajorVersion() < 21)
|
||||||
{
|
{
|
||||||
await Utilities.AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
await Utilities.AppHelpers.CipherListOptions(Page, cipher);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -197,7 +195,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await Utilities.AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
await Utilities.AppHelpers.CipherListOptions(Page, cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
|||||||
@@ -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,27 +45,29 @@
|
|||||||
|
|
||||||
<DataTemplate x:Key="groupTemplate"
|
<DataTemplate x:Key="groupTemplate"
|
||||||
x:DataType="pages:GroupingsPageListItem">
|
x:DataType="pages:GroupingsPageListItem">
|
||||||
<controls:ExtendedStackLayout Orientation="Horizontal"
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform">
|
<StackLayout Orientation="Horizontal"
|
||||||
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
StyleClass="list-row, list-row-platform">
|
||||||
HorizontalOptions="Start"
|
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
|
||||||
VerticalOptions="Center"
|
HorizontalOptions="Start"
|
||||||
StyleClass="list-icon, list-icon-platform">
|
VerticalOptions="Center"
|
||||||
<controls:FaLabel.Effects>
|
StyleClass="list-icon, list-icon-platform">
|
||||||
<effects:FixedSizeEffect />
|
<controls:FaLabel.Effects>
|
||||||
</controls:FaLabel.Effects>
|
<effects:FixedSizeEffect />
|
||||||
</controls:FaLabel>
|
</controls:FaLabel.Effects>
|
||||||
<Label Text="{Binding Name, Mode=OneWay}"
|
</controls:FaLabel>
|
||||||
LineBreakMode="TailTruncation"
|
<Label Text="{Binding Name, Mode=OneWay}"
|
||||||
HorizontalOptions="FillAndExpand"
|
LineBreakMode="TailTruncation"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
StyleClass="list-title"/>
|
VerticalOptions="CenterAndExpand"
|
||||||
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
StyleClass="list-title"/>
|
||||||
HorizontalOptions="End"
|
<Label Text="{Binding ItemCount, Mode=OneWay}"
|
||||||
VerticalOptions="CenterAndExpand"
|
HorizontalOptions="End"
|
||||||
HorizontalTextAlignment="End"
|
VerticalOptions="CenterAndExpand"
|
||||||
StyleClass="list-sub"/>
|
HorizontalTextAlignment="End"
|
||||||
</controls:ExtendedStackLayout>
|
StyleClass="list-sub"/>
|
||||||
|
</StackLayout>
|
||||||
|
</ViewCell>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
|
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
|
||||||
@@ -87,21 +89,27 @@
|
|||||||
IsVisible="{Binding ShowAddCipherButton}"></Button>
|
IsVisible="{Binding ShowAddCipherButton}"></Button>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<RefreshView
|
<controls:ExtendedListView
|
||||||
|
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}"
|
||||||
Command="{Binding RefreshCommand}">
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
<controls:ExtendedCollectionView
|
IsGroupingEnabled="True"
|
||||||
ItemsSource="{Binding GroupedItems}"
|
ItemSelected="RowSelected"
|
||||||
VerticalOptions="FillAndExpand"
|
StyleClass="list, list-platform">
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
<x:Arguments>
|
||||||
IsGrouped="True"
|
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
|
||||||
SelectionMode="Single"
|
</x:Arguments>
|
||||||
SelectionChanged="RowSelected"
|
|
||||||
StyleClass="list, list-platform">
|
|
||||||
|
|
||||||
<CollectionView.GroupHeaderTemplate>
|
<ListView.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">
|
||||||
@@ -118,10 +126,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>
|
||||||
</DataTemplate>
|
</ViewCell>
|
||||||
</CollectionView.GroupHeaderTemplate>
|
</DataTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView.GroupHeaderTemplate>
|
||||||
</RefreshView>
|
</controls:ExtendedListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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;
|
||||||
@@ -8,7 +9,6 @@ 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,6 +33,7 @@ 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");
|
||||||
@@ -72,6 +73,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExtendedListView ListView { get; set; }
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -194,14 +197,14 @@ namespace Bit.App.Pages
|
|||||||
_vm.DisableRefreshing();
|
_vm.DisableRefreshing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(e.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item))
|
if (!(e.SelectedItem 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 ? "\uf115" : "\uf07c"; // fa-folder-open-o : fa-folder-open
|
_icon = Folder.Id == null ? "" : "";
|
||||||
}
|
}
|
||||||
else if (Collection != null)
|
else if (Collection != null)
|
||||||
{
|
{
|
||||||
_icon = "\uf1b2"; // fa-cube
|
_icon = "";
|
||||||
}
|
}
|
||||||
else if (Type != null)
|
else if (Type != null)
|
||||||
{
|
{
|
||||||
switch (Type.Value)
|
switch (Type.Value)
|
||||||
{
|
{
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
_icon = "\uf0ac"; // fa-globe
|
_icon = "";
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
_icon = "\uf24a"; // fa-sticky-note-o
|
_icon = "";
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
_icon = "\uf09d"; // fa-credit-card
|
_icon = "";
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
_icon = "\uf2c3"; // fa-id-card-o
|
_icon = "";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_icon = "\uf0ac"; // fa-globe
|
_icon = "";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -61,7 +60,6 @@ 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;
|
||||||
@@ -516,7 +514,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if ((Page as BaseContentPage).DoOnce())
|
if ((Page as BaseContentPage).DoOnce())
|
||||||
{
|
{
|
||||||
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
|
await AppHelpers.CipherListOptions(Page, cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,53 +34,57 @@
|
|||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<controls:ExtendedCollectionView
|
<ListView x:Name="_listView"
|
||||||
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">
|
||||||
<CollectionView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:PasswordHistoryView">
|
<DataTemplate x:DataType="views:PasswordHistoryView">
|
||||||
<Grid
|
<ViewCell>
|
||||||
StyleClass="list-row, list-row-platform"
|
<Grid
|
||||||
Padding="10"
|
StyleClass="list-row, list-row-platform"
|
||||||
RowSpacing="0"
|
Padding="10"
|
||||||
ColumnSpacing="10">
|
RowSpacing="0"
|
||||||
|
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>
|
||||||
</CollectionView.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</controls:ExtendedCollectionView>
|
</ListView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
Title="{u:I18n ScanQrTitle}">
|
Title="{u:I18n ScanQrTitle}">
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Move}" Clicked="Save_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MoveToOrgDesc}"
|
Text="{u:I18n ShareDesc}"
|
||||||
StyleClass="box-footer-label" />
|
StyleClass="box-footer-label" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box"
|
<StackLayout StyleClass="box"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace Bit.App.Pages
|
|||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||||
OrganizationOptions = new List<KeyValuePair<string, string>>();
|
OrganizationOptions = new List<KeyValuePair<string, string>>();
|
||||||
PageTitle = AppResources.MoveToOrganization;
|
PageTitle = AppResources.Share;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CipherId { get; set; }
|
public string CipherId { get; set; }
|
||||||
@@ -109,9 +109,7 @@ namespace Bit.App.Pages
|
|||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
|
||||||
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
_platformUtilsService.ShowToast("success", null, AppResources.ItemShared);
|
||||||
(await _userService.GetOrganizationAsync(OrganizationId)).Name);
|
|
||||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
|
||||||
await Page.Navigation.PopModalAsync();
|
await Page.Navigation.PopModalAsync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
x:Name="_collectionsItem"
|
x:Name="_collectionsItem"
|
||||||
Clicked="Collections_Clicked"
|
Clicked="Collections_Clicked"
|
||||||
Order="Secondary" />
|
Order="Secondary" />
|
||||||
<ToolbarItem Text="{u:I18n MoveToOrganization}"
|
<ToolbarItem Text="{u:I18n Share}"
|
||||||
x:Key="shareItem"
|
x:Key="shareItem"
|
||||||
x:Name="_shareItem"
|
x:Name="_shareItem"
|
||||||
Clicked="Share_Clicked"
|
Clicked="Share_Clicked"
|
||||||
@@ -224,41 +224,24 @@
|
|||||||
<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" />
|
||||||
<controls:MonoLabel
|
<Label
|
||||||
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="2"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n CopyNumber}" />
|
AutomationProperties.Name="{u:I18n CopyNumber}" />
|
||||||
|
|||||||
@@ -128,10 +128,6 @@ 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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,10 +142,6 @@ 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));
|
||||||
}
|
}
|
||||||
@@ -159,10 +151,6 @@ 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));
|
||||||
}
|
}
|
||||||
@@ -172,10 +160,6 @@ 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();
|
||||||
@@ -187,10 +171,6 @@ 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));
|
||||||
}
|
}
|
||||||
@@ -200,10 +180,6 @@ 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));
|
||||||
}
|
}
|
||||||
@@ -220,7 +196,7 @@ namespace Bit.App.Pages
|
|||||||
if (_vm.Cipher.OrganizationId == null)
|
if (_vm.Cipher.OrganizationId == null)
|
||||||
{
|
{
|
||||||
options.Add(AppResources.Clone);
|
options.Add(AppResources.Clone);
|
||||||
options.Add(AppResources.MoveToOrganization);
|
options.Add(AppResources.Share);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -229,12 +205,6 @@ 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())
|
||||||
@@ -252,7 +222,7 @@ namespace Bit.App.Pages
|
|||||||
var page = new CollectionsPage(_vm.CipherId);
|
var page = new CollectionsPage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.MoveToOrganization)
|
else if (selection == AppResources.Share)
|
||||||
{
|
{
|
||||||
var page = new SharePage(_vm.CipherId);
|
var page = new SharePage(_vm.CipherId);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
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;
|
||||||
@@ -24,12 +23,10 @@ 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;
|
||||||
@@ -39,7 +36,6 @@ 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()
|
||||||
{
|
{
|
||||||
@@ -51,13 +47,11 @@ 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);
|
||||||
@@ -70,7 +64,6 @@ 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; }
|
||||||
@@ -116,15 +109,6 @@ 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;
|
||||||
@@ -204,7 +188,6 @@ 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
|
||||||
{
|
{
|
||||||
@@ -243,7 +226,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(this, Cipher, f)).ToList();
|
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(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))
|
||||||
@@ -276,13 +259,8 @@ namespace Bit.App.Pages
|
|||||||
_totpInterval = null;
|
_totpInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
if (! await PromptPasswordAsync())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
if (ShowPassword)
|
if (ShowPassword)
|
||||||
{
|
{
|
||||||
@@ -290,26 +268,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ToggleCardNumber()
|
public void ToggleCardCode()
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -514,11 +474,6 @@ namespace Bit.App.Pages
|
|||||||
canOpenFile = false;
|
canOpenFile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await PromptPasswordAsync())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -609,11 +564,6 @@ 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")
|
||||||
{
|
{
|
||||||
@@ -688,28 +638,16 @@ 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(ViewPageViewModel vm, CipherView cipher, FieldView field)
|
public ViewPageFieldViewModel(CipherView cipher, FieldView field)
|
||||||
{
|
{
|
||||||
_vm = vm;
|
|
||||||
_cipher = cipher;
|
_cipher = cipher;
|
||||||
Field = field;
|
Field = field;
|
||||||
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
||||||
@@ -750,12 +688,8 @@ 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 async void ToggleHiddenValue()
|
public void ToggleHiddenValue()
|
||||||
{
|
{
|
||||||
if (!await _vm.PromptPasswordAsync())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShowHiddenValue = !ShowHiddenValue;
|
ShowHiddenValue = !ShowHiddenValue;
|
||||||
if (ShowHiddenValue)
|
if (ShowHiddenValue)
|
||||||
{
|
{
|
||||||
|
|||||||
66
src/App/Resources/AppResources.Designer.cs
generated
66
src/App/Resources/AppResources.Designer.cs
generated
@@ -365,12 +365,6 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Move {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("Move", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Saving {
|
public static string Saving {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("Saving", resourceCulture);
|
return ResourceManager.GetString("Saving", resourceCulture);
|
||||||
@@ -1265,9 +1259,9 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LearnOrg {
|
public static string ShareVault {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("LearnOrg", resourceCulture);
|
return ResourceManager.GetString("ShareVault", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2405,12 +2399,6 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string MovedItemToOrg {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("MovedItemToOrg", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ItemShared {
|
public static string ItemShared {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ItemShared", resourceCulture);
|
return ResourceManager.GetString("ItemShared", resourceCulture);
|
||||||
@@ -2435,21 +2423,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string MoveToOrganization {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("MoveToOrganization", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string NoOrgsToList {
|
public static string NoOrgsToList {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("NoOrgsToList", resourceCulture);
|
return ResourceManager.GetString("NoOrgsToList", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string MoveToOrgDesc {
|
public static string ShareDesc {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("MoveToOrgDesc", resourceCulture);
|
return ResourceManager.GetString("ShareDesc", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2501,9 +2483,9 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LearnOrgConfirmation {
|
public static string ShareVaultConfirmation {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("LearnOrgConfirmation", resourceCulture);
|
return ResourceManager.GetString("ShareVaultConfirmation", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3389,12 +3371,6 @@ 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);
|
||||||
@@ -3532,35 +3508,5 @@ namespace Bit.App.Resources {
|
|||||||
return ResourceManager.GetString("SendFileEmailVerificationRequired", resourceCulture);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CaptchaRequired {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("CaptchaRequired", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CaptchaFailed {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("CaptchaFailed", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -604,7 +604,7 @@
|
|||||||
<comment>Confirmation, like "Ok, I understand it"</comment>
|
<comment>Confirmation, like "Ok, I understand it"</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="OptionDefaults" xml:space="preserve">
|
<data name="OptionDefaults" xml:space="preserve">
|
||||||
<value>Прадвызначаныя налады вызначаюцца з генератара пароляў асноўнай праграмы Bitwarden.</value>
|
<value>Налады па змаўчанні задаюцца з генератара пароляў асноўнай праграмы Bitwarden.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options" xml:space="preserve">
|
<data name="Options" xml:space="preserve">
|
||||||
<value>Параметры</value>
|
<value>Параметры</value>
|
||||||
@@ -759,7 +759,7 @@
|
|||||||
<value>Стан</value>
|
<value>Стан</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
|
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
|
||||||
<value>Самы просты спосаб дадаць новыя лагіны ў ваша сховішча - служба аўтазапаўнення Bitwarden. Дадатковыя звесткі пра выкарыстанне службы аўтазапаўнення Bitwarden можна знайсці ў раздзеле "Налады".</value>
|
<value>Самы просты спосаб дадаць новыя імёны карыстальінка ў ваша сховішча гэта служба аўтазапаўнення Bitwarden. Дадатковыя звесткі пра выкарыстанне службы аўтазапаўнення Bitwarden знаходзяцца ў раздзеле 'Налады'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Autofill" xml:space="preserve">
|
<data name="Autofill" xml:space="preserve">
|
||||||
<value>Аўтазапаўненне</value>
|
<value>Аўтазапаўненне</value>
|
||||||
@@ -1203,7 +1203,7 @@
|
|||||||
<value>Асноўны дамен</value>
|
<value>Асноўны дамен</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Default" xml:space="preserve">
|
<data name="Default" xml:space="preserve">
|
||||||
<value>Прадвызначана</value>
|
<value>Па змаўчанні</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Exact" xml:space="preserve">
|
<data name="Exact" xml:space="preserve">
|
||||||
<value>Дакладна</value>
|
<value>Дакладна</value>
|
||||||
@@ -1487,11 +1487,11 @@
|
|||||||
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
|
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultUriMatchDetection" xml:space="preserve">
|
<data name="DefaultUriMatchDetection" xml:space="preserve">
|
||||||
<value>Прадвызначанае выяўленне супадзення URI</value>
|
<value>Выяўленне супадзення URI па змаўчанні</value>
|
||||||
<comment>Default URI match detection for auto-fill.</comment>
|
<comment>Default URI match detection for auto-fill.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
|
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
|
||||||
<value>Выберыце прадвызначаны спосаб, які выкарыстоўваецца пры вызначэнні адпаведнасці URI для ўліковых даных пры выкананні такіх дзеянняў, як аўтаматычнае запаўненне.</value>
|
<value>Выберыце спосаб па змаўчанні, які выкарыстоўваецца пры вызначэнні адпаведнасці URI для ўліковых даных пры выкананні такіх дзеянняў, як аўтаматычнае запаўненне.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Theme" xml:space="preserve">
|
<data name="Theme" xml:space="preserve">
|
||||||
<value>Тэма</value>
|
<value>Тэма</value>
|
||||||
@@ -1527,7 +1527,7 @@
|
|||||||
<value>URI у чорным спісе</value>
|
<value>URI у чорным спісе</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="BlacklistedUrisDescription" xml:space="preserve">
|
<data name="BlacklistedUrisDescription" xml:space="preserve">
|
||||||
<value>Аўтазапаўненне не будзе прапаноўвацца для URI з чорнага спіса. Элементы гэтага спіса трэба падзяляць коскай. Напрыклад: "https://twitter.com, androidapp://com.twitter.android".</value>
|
<value>Аўтазапаўненне не прапануецца для URI з чорнага спісу. Элементы гэтага спісу павінны быць раздзелены коскай. Напрыклад: "https://twitter.com, androidapp://com.twitter.android".</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisableSavePrompt" xml:space="preserve">
|
<data name="DisableSavePrompt" xml:space="preserve">
|
||||||
<value>Выключыць запыт на захаванне.</value>
|
<value>Выключыць запыт на захаванне.</value>
|
||||||
@@ -1709,7 +1709,7 @@
|
|||||||
<value>Часова немагчыма зайсці праз адзіны ўваход (SSO).</value>
|
<value>Часова немагчыма зайсці праз адзіны ўваход (SSO).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SetMasterPassword" xml:space="preserve">
|
<data name="SetMasterPassword" xml:space="preserve">
|
||||||
<value>Прызначыць асноўны пароль</value>
|
<value>Задаць асноўны пароль</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SetMasterPasswordSummary" xml:space="preserve">
|
<data name="SetMasterPasswordSummary" xml:space="preserve">
|
||||||
<value>Для завяршэння ўваходу праз SSO, усталюйце галоўны пароль для доступу і абароны вашаго сховішча.</value>
|
<value>Для завяршэння ўваходу праз SSO, усталюйце галоўны пароль для доступу і абароны вашаго сховішча.</value>
|
||||||
@@ -1766,7 +1766,7 @@
|
|||||||
<value>Выкарыстоўваць убудаванае аўтазапаўненне</value>
|
<value>Выкарыстоўваць убудаванае аўтазапаўненне</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InlineAutofillDescription" xml:space="preserve">
|
<data name="InlineAutofillDescription" xml:space="preserve">
|
||||||
<value>Выкарыстоўваць убудаванае аўтазапаўненне, калі выбраны спосаб уводу (клавіятура) яго падтрымлівае. Калі вашая канфігурацыя не падтрымліваецца (або гэты параметр адключаны), будзе выкарыстоўвацца прадвызначанае аўтазапаўненне.</value>
|
<value>Выкарыстоўваць убудаванае аўтазапаўненне, калі выбраны спосаб уводу (клавіятура) яго падтрымлівае. Калі вашая канфігурацыя не падтрымліваецца (або гэты параметр адключаны), будзе выкарыстоўвацца аўтазапаўненне накладаннем па змаўчанні.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Accessibility" xml:space="preserve">
|
<data name="Accessibility" xml:space="preserve">
|
||||||
<value>Спецыяльныя магчымасці</value>
|
<value>Спецыяльныя магчымасці</value>
|
||||||
@@ -1802,55 +1802,55 @@
|
|||||||
<value>An organization policy is affecting your ownership options.</value>
|
<value>An organization policy is affecting your ownership options.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Send" xml:space="preserve">
|
<data name="Send" xml:space="preserve">
|
||||||
<value>Адпраўленне</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="AllSends" xml:space="preserve">
|
<data name="AllSends" xml:space="preserve">
|
||||||
<value>Усе адпраўленні</value>
|
<value>All 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">
|
||||||
<value>Адпраўленні</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="NameInfo" xml:space="preserve">
|
<data name="NameInfo" xml:space="preserve">
|
||||||
<value>Зразумелая назва для апісання адпраўлення</value>
|
<value>A friendly name to describe this 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>Тэкст</value>
|
<value>Text</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeTextInfo" xml:space="preserve">
|
<data name="TypeTextInfo" xml:space="preserve">
|
||||||
<value>Тэкст, які вы хочаце адправіць.</value>
|
<value>The text you want to send.</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>When accessing the Send, hide the text by default</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>Файл</value>
|
<value>File</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>Файл, які вы хочаце адправіць.</value>
|
<value>The file you want to send.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Дата выдалення</value>
|
<value>Deletion Date</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionTime" xml:space="preserve">
|
<data name="DeletionTime" xml:space="preserve">
|
||||||
<value>Час выдалення</value>
|
<value>Deletion Time</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>Гэтае адпраўленне аўтаматычна выдаліцца ў вызначаную дату і час.</value>
|
<value>The Send will be permanently deleted on the specified date and time.</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>Чаканне выдалення</value>
|
<value>Pending deletion</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDate" xml:space="preserve">
|
<data name="ExpirationDate" xml:space="preserve">
|
||||||
<value>Дата завяршэння</value>
|
<value>Expiration Date</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationTime" xml:space="preserve">
|
<data name="ExpirationTime" xml:space="preserve">
|
||||||
<value>Час завяршэння</value>
|
<value>Expiration Time</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>If set, access to this Send will expire on the specified date and time.</value>
|
||||||
@@ -1900,103 +1900,75 @@
|
|||||||
<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>Дадаць 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>Капіяваць спасылку</value>
|
<value>Copy Link</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareLink" xml:space="preserve">
|
<data name="ShareLink" xml:space="preserve">
|
||||||
<value>Падзяліцца спасылкай</value>
|
<value>Share Link</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendLink" xml:space="preserve">
|
<data name="SendLink" xml:space="preserve">
|
||||||
<value>Спасылка на адпраўленне</value>
|
<value>Send link</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>Пошук адпраўленняў</value>
|
<value>Search 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>Рэдагаваць адпраўленне</value>
|
<value>Edit 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>Дадаць адпраўленне</value>
|
<value>Add 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>Сапраўды хочаце выдаліць адпраўленне?</value>
|
<value>Are you sure you want to delete this 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>Адпраўленне выдалена.</value>
|
<value>Send has been deleted.</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>Адпраўленне абноўлена.</value>
|
<value>Send updated.</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>Новае адпраўленне створана.</value>
|
<value>New send created.</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 дзень</value>
|
<value>1 day</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoDays" xml:space="preserve">
|
<data name="TwoDays" xml:space="preserve">
|
||||||
<value>2 дні</value>
|
<value>2 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThreeDays" xml:space="preserve">
|
<data name="ThreeDays" xml:space="preserve">
|
||||||
<value>3 дні</value>
|
<value>3 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SevenDays" xml:space="preserve">
|
<data name="SevenDays" xml:space="preserve">
|
||||||
<value>7 дзён</value>
|
<value>7 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThirtyDays" xml:space="preserve">
|
<data name="ThirtyDays" xml:space="preserve">
|
||||||
<value>30 дзён</value>
|
<value>30 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Custom" xml:space="preserve">
|
<data name="Custom" xml:space="preserve">
|
||||||
<value>Адвольны</value>
|
<value>Custom</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareOnSave" xml:space="preserve">
|
<data name="ShareOnSave" xml:space="preserve">
|
||||||
<value>Падзяліцца адпраўленнем пасля захавання.</value>
|
<value>Share this Send upon save.</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>Згодна з карпаратыўнай палітыкай вы можаце выдаліць толькі існае адпраўленне.</value>
|
<value>Due to an enterprise policy, you are only able to delete an existing 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>Пра адпраўленне</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>Хаваць адрас электроннай пошты ад атрымальнікаў.</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>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>Для адпраўлення файлаў патрэбна пацвердзіць свой адрас электроннай пошты.</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>Паўторны запыт галоўнага пароля</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordConfirmation" xml:space="preserve">
|
|
||||||
<value>Пацверджанне галоўнага пароля</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
|
||||||
<value>Гэтае дзеянне ахоўваецца, для працягу зноў увядзіце пароль для верыфікацыі асобы.</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>При изнасяне данните се шифрират с ключа ви. Ако го смените, ще трябва наново да ги изнесете, защото няма да може да дешифрирате настоящия файл.</value>
|
<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>
|
||||||
</data>
|
</data>
|
||||||
<data name="EncExportAccountWarning" xml:space="preserve">
|
<data name="EncExportAccountWarning" xml:space="preserve">
|
||||||
<value>Ключовете за шифриране са уникални за всеки потребител, затова не може да внесете шифрирани данни от един потребител в регистрацията на друг.</value>
|
<value>Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
|
||||||
<value>Потвърждаване на изнасянето на трезора</value>
|
<value>Потвърждаване на изнасянето на трезора</value>
|
||||||
@@ -1802,201 +1802,173 @@
|
|||||||
<value>Политика от някоя организация влияе на вариантите за собственост.</value>
|
<value>Политика от някоя организация влияе на вариантите за собственост.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Send" xml:space="preserve">
|
<data name="Send" xml:space="preserve">
|
||||||
<value>Изпращане</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="AllSends" xml:space="preserve">
|
<data name="AllSends" xml:space="preserve">
|
||||||
<value>Всички изпращания</value>
|
<value>All 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">
|
||||||
<value>Изпращания</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="NameInfo" xml:space="preserve">
|
<data name="NameInfo" xml:space="preserve">
|
||||||
<value>Описателно име за това изпращане.</value>
|
<value>A friendly name to describe this 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>Текст</value>
|
<value>Text</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeTextInfo" xml:space="preserve">
|
<data name="TypeTextInfo" xml:space="preserve">
|
||||||
<value>Текст за изпращане.</value>
|
<value>The text you want to send.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="HideTextByDefault" xml:space="preserve">
|
<data name="HideTextByDefault" xml:space="preserve">
|
||||||
<value>При достъп до изпращането стандартно текстът да се скрива</value>
|
<value>When accessing the Send, hide the text by default</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>Файл</value>
|
<value>File</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TypeFileInfo" xml:space="preserve">
|
<data name="TypeFileInfo" xml:space="preserve">
|
||||||
<value>Файл за изпращане.</value>
|
<value>The file you want to send.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDate" xml:space="preserve">
|
<data name="DeletionDate" xml:space="preserve">
|
||||||
<value>Дата на изтриване</value>
|
<value>Deletion Date</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionTime" xml:space="preserve">
|
<data name="DeletionTime" xml:space="preserve">
|
||||||
<value>Време на изтриване</value>
|
<value>Deletion Time</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeletionDateInfo" xml:space="preserve">
|
<data name="DeletionDateInfo" xml:space="preserve">
|
||||||
<value>Изпращането ще бъде окончателно изтрито на зададената дата и време.</value>
|
<value>The Send will be permanently deleted on the specified date and time.</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>Предстои изтриване</value>
|
<value>Pending deletion</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDate" xml:space="preserve">
|
<data name="ExpirationDate" xml:space="preserve">
|
||||||
<value>Дата на валидност</value>
|
<value>Expiration Date</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationTime" xml:space="preserve">
|
<data name="ExpirationTime" xml:space="preserve">
|
||||||
<value>Време на валидност</value>
|
<value>Expiration Time</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExpirationDateInfo" xml:space="preserve">
|
<data name="ExpirationDateInfo" xml:space="preserve">
|
||||||
<value>При задаване — това изпращане ще се изключи на зададената дата и време.</value>
|
<value>If set, access to this Send will expire on the specified date and time.</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>Изтекъл</value>
|
<value>Expired</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCount" xml:space="preserve">
|
<data name="MaximumAccessCount" xml:space="preserve">
|
||||||
<value>Максимален брой достъпвания.</value>
|
<value>Maximum Access Count</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
<data name="MaximumAccessCountInfo" xml:space="preserve">
|
||||||
<value>При задаване — това изпращане ще се изключи след определен брой достъпвания.</value>
|
<value>If set, users will no longer be able to access this send once the maximum access count is reached.</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>Достигнат е максималният брой достъпвания</value>
|
<value>Max access count reached</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CurrentAccessCount" xml:space="preserve">
|
<data name="CurrentAccessCount" xml:space="preserve">
|
||||||
<value>Текущ брой на достъпванията</value>
|
<value>Current Access Count</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NewPassword" xml:space="preserve">
|
<data name="NewPassword" xml:space="preserve">
|
||||||
<value>Нова парола</value>
|
<value>New Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordInfo" xml:space="preserve">
|
<data name="PasswordInfo" xml:space="preserve">
|
||||||
<value>Изискване на парола за достъп до това изпращане.</value>
|
<value>Optionally require a password for users to access this 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>Премахване на парола</value>
|
<value>Remove Password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
|
||||||
<value>Сигурни ли сте, че искате да премахнете паролата?</value>
|
<value>Are you sure you want to remove the password?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemovingSendPassword" xml:space="preserve">
|
<data name="RemovingSendPassword" xml:space="preserve">
|
||||||
<value>Премахване на парола</value>
|
<value>Removing password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendPasswordRemoved" xml:space="preserve">
|
<data name="SendPasswordRemoved" xml:space="preserve">
|
||||||
<value>Паролата е премахната.</value>
|
<value>Password has been removed.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="NotesInfo" xml:space="preserve">
|
<data name="NotesInfo" xml:space="preserve">
|
||||||
<value>Скрити бележки за това изпращане.</value>
|
<value>Private notes about this 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>Пълно спиране на това изпращане — никой няма да има достъп.</value>
|
<value>Disable this Send so that no one can access it.</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>Няма изпращания.</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>Добавяне на изпращане</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>Копиране на връзката</value>
|
<value>Copy Link</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareLink" xml:space="preserve">
|
<data name="ShareLink" xml:space="preserve">
|
||||||
<value>Споделяне на връзката</value>
|
<value>Share Link</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SendLink" xml:space="preserve">
|
<data name="SendLink" xml:space="preserve">
|
||||||
<value>Изпращане на връзката</value>
|
<value>Send link</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>Търсене в изпратените</value>
|
<value>Search 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>Редактиране на изпращане</value>
|
<value>Edit 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>Добавяне на изпращане</value>
|
<value>Add 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>Сигурни ли сте, че искате да изтриете това изпращане?</value>
|
<value>Are you sure you want to delete this 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>Изпратеното е изтрито.</value>
|
<value>Send has been deleted.</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>Изпратеното е редактирано.</value>
|
<value>Send updated.</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>Създадено е ново изпращане.</value>
|
<value>New send created.</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 ден</value>
|
<value>1 day</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoDays" xml:space="preserve">
|
<data name="TwoDays" xml:space="preserve">
|
||||||
<value>2 дни</value>
|
<value>2 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThreeDays" xml:space="preserve">
|
<data name="ThreeDays" xml:space="preserve">
|
||||||
<value>3 дни</value>
|
<value>3 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SevenDays" xml:space="preserve">
|
<data name="SevenDays" xml:space="preserve">
|
||||||
<value>7 дни</value>
|
<value>7 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ThirtyDays" xml:space="preserve">
|
<data name="ThirtyDays" xml:space="preserve">
|
||||||
<value>30 дни</value>
|
<value>30 days</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Custom" xml:space="preserve">
|
<data name="Custom" xml:space="preserve">
|
||||||
<value>По избор</value>
|
<value>Custom</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ShareOnSave" xml:space="preserve">
|
<data name="ShareOnSave" xml:space="preserve">
|
||||||
<value>Споделяне на изпратеното при запис.</value>
|
<value>Share this Send upon save.</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>Поради политика на организация, може само да изтривате съществуващи изпращания.</value>
|
<value>Due to an enterprise policy, you are only able to delete an existing 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>Относно изпращането</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>Скриване на е-пощата ми от получателите.</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>Повторно запитване за главната парола</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordConfirmation" xml:space="preserve">
|
|
||||||
<value>Потвърждение на главната парола</value>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordConfirmationDesc" xml:space="preserve">
|
|
||||||
<value>Това действие е защитено. За да продължите, въведете отново главната си парола, за да потвърдите самоличността си.</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1901,11 +1901,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="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">
|
||||||
@@ -1976,28 +1972,4 @@
|
|||||||
<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>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user