Compare commits
380 Commits
v2.4.3
...
feature/io
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69cd7d79e7 | ||
|
|
dbf94c1b56 | ||
|
|
4b0fb2840e | ||
|
|
629c696c81 | ||
|
|
bf1aa7c4eb | ||
|
|
318a3e4de9 | ||
|
|
0f992d27b3 | ||
|
|
83fd6736f6 | ||
|
|
397250368a | ||
|
|
5e4365084b | ||
|
|
ea5e4aafa3 | ||
|
|
69d1de47c6 | ||
|
|
0d3f819e93 | ||
|
|
3760e0f9f4 | ||
|
|
5a13cb53ba | ||
|
|
0e9cbe4539 | ||
|
|
b8c1107c94 | ||
|
|
a07ef1a1d6 | ||
|
|
99ccd62bcd | ||
|
|
bfb050a6f9 | ||
|
|
4e0b05571d | ||
|
|
d93d70fd66 | ||
|
|
41098ff05b | ||
|
|
4ed7491116 | ||
|
|
1ebad6bca5 | ||
|
|
48e3986264 | ||
|
|
88a1d8d4e8 | ||
|
|
f3ff991abe | ||
|
|
17b89dc21c | ||
|
|
ff76a3ec15 | ||
|
|
3a2e012c42 | ||
|
|
a0bb16c35f | ||
|
|
62a8d1c017 | ||
|
|
ce4e3ed1cd | ||
|
|
4669275680 | ||
|
|
fc1000acc1 | ||
|
|
c9ce7256e5 | ||
|
|
34aba0e168 | ||
|
|
9e9e2e12d8 | ||
|
|
3eec349038 | ||
|
|
69650a1ab5 | ||
|
|
faac7ebe5e | ||
|
|
d3734c63fc | ||
|
|
4aad34cd75 | ||
|
|
73eb3c2c1e | ||
|
|
6109091ec0 | ||
|
|
c0783cd162 | ||
|
|
a9a4fa56c1 | ||
|
|
271e6b3d92 | ||
|
|
750faf8a83 | ||
|
|
716e52f6ff | ||
|
|
010a4210f4 | ||
|
|
8d23bc89e8 | ||
|
|
f2857397f0 | ||
|
|
6023374fbe | ||
|
|
d3c1b58c2a | ||
|
|
a026af2072 | ||
|
|
51be6e522b | ||
|
|
024d9380c9 | ||
|
|
14b51b1a7f | ||
|
|
4667a9d643 | ||
|
|
d3f00340fb | ||
|
|
8866fc6322 | ||
|
|
68887c5de7 | ||
|
|
99b67b680c | ||
|
|
9b5bf4306f | ||
|
|
be55504b01 | ||
|
|
307a5a5843 | ||
|
|
d050215ebc | ||
|
|
67e26c778b | ||
|
|
efb10d155d | ||
|
|
913c673773 | ||
|
|
339decafe4 | ||
|
|
380c3583fb | ||
|
|
244a6a7f41 | ||
|
|
745b54bf2a | ||
|
|
4f86bb2043 | ||
|
|
ada8a73849 | ||
|
|
1e3204f91d | ||
|
|
b5c6a57fa0 | ||
|
|
6ca5b66aa7 | ||
|
|
24a0396d0f | ||
|
|
7f7b673b0a | ||
|
|
f79ff3fd63 | ||
|
|
2f2fa8a25b | ||
|
|
9042b1009e | ||
|
|
dbc0f490c5 | ||
|
|
6d8f627772 | ||
|
|
b4c016c9d4 | ||
|
|
ae763ebca8 | ||
|
|
880483ac79 | ||
|
|
f44e6ab75f | ||
|
|
10a718b0c7 | ||
|
|
9ec4050e4d | ||
|
|
93e2c0df7c | ||
|
|
8d07397a59 | ||
|
|
15d44b873b | ||
|
|
6a979d0ff5 | ||
|
|
a4db088eda | ||
|
|
96454b7cbf | ||
|
|
9c1df2179c | ||
|
|
52c9125404 | ||
|
|
172a857604 | ||
|
|
d8e68a266c | ||
|
|
1f57ba6c50 | ||
|
|
ff19578807 | ||
|
|
9298d57f22 | ||
|
|
8cf5d5728e | ||
|
|
bdf6d764ca | ||
|
|
05e8da4bcc | ||
|
|
382e547f74 | ||
|
|
4f9985d2b0 | ||
|
|
ef97417cd7 | ||
|
|
7bdf4d8b18 | ||
|
|
a6c95d06b5 | ||
|
|
bd4a275558 | ||
|
|
a2b46ee7cb | ||
|
|
2003ac9d2c | ||
|
|
2a5667251e | ||
|
|
79589b07fc | ||
|
|
0aed13a2cf | ||
|
|
df412e75d1 | ||
|
|
2b8dbde923 | ||
|
|
33791a03ac | ||
|
|
80a33e98a2 | ||
|
|
afed18908b | ||
|
|
fe58dea3e0 | ||
|
|
569045fcd5 | ||
|
|
fdda670311 | ||
|
|
fbb7b05b9c | ||
|
|
976eeab6d7 | ||
|
|
e61bcd2785 | ||
|
|
570edb4319 | ||
|
|
8fe8c42765 | ||
|
|
0eebe6b156 | ||
|
|
946831b37e | ||
|
|
1d4e742d66 | ||
|
|
29979f6b04 | ||
|
|
2f6e1ff477 | ||
|
|
c1030c48fa | ||
|
|
ef5d08cb75 | ||
|
|
faa6904ce3 | ||
|
|
c27da8e7c4 | ||
|
|
3ef5ca9cc0 | ||
|
|
2fbd3b4538 | ||
|
|
b3b21ea6b1 | ||
|
|
a3b4ede8f3 | ||
|
|
10ea6a86e3 | ||
|
|
3b2b37b3b0 | ||
|
|
75e27ffbe3 | ||
|
|
a2cff6da28 | ||
|
|
fe80fd0ba1 | ||
|
|
5c6b9fa471 | ||
|
|
d926565358 | ||
|
|
ce0b8bc62d | ||
|
|
04aeddc5de | ||
|
|
13ffbe911a | ||
|
|
ab04759b0e | ||
|
|
798cfef391 | ||
|
|
3b5cdfe03c | ||
|
|
63449a3832 | ||
|
|
23011aa8ae | ||
|
|
654d71cbbc | ||
|
|
d0e424abd9 | ||
|
|
5cee71ce8a | ||
|
|
c3be4f44a4 | ||
|
|
ff3ac10bc3 | ||
|
|
37bee011dc | ||
|
|
3c7029bdc8 | ||
|
|
29369b7bb2 | ||
|
|
f2b9214836 | ||
|
|
8b7b8a5e43 | ||
|
|
142d056393 | ||
|
|
2b81bd2c8a | ||
|
|
53c82f23bf | ||
|
|
9b621bd1d0 | ||
|
|
9165cb2b0e | ||
|
|
2c13cef17c | ||
|
|
1098686d51 | ||
|
|
6fa23475e3 | ||
|
|
685548ab72 | ||
|
|
3799eb4603 | ||
|
|
20d5c6a63a | ||
|
|
ce11232cbe | ||
|
|
233319a0a3 | ||
|
|
7cf64ff088 | ||
|
|
a8acd36b1e | ||
|
|
d88695f5d5 | ||
|
|
5e70d03dbe | ||
|
|
2602a09443 | ||
|
|
a18e59a28a | ||
|
|
52ba9f2ba7 | ||
|
|
8d5614cd7b | ||
|
|
9b6bf136f1 | ||
|
|
10677f3705 | ||
|
|
9d4d09810a | ||
|
|
28a1bd5219 | ||
|
|
1197c10592 | ||
|
|
3bb92d452b | ||
|
|
2bfabfd838 | ||
|
|
9876cd547f | ||
|
|
4a8d261a82 | ||
|
|
c4823f1c37 | ||
|
|
6e9238329c | ||
|
|
c86cb962b9 | ||
|
|
56935a7210 | ||
|
|
cdc08e7e8a | ||
|
|
ca7794e6f2 | ||
|
|
3b5cae01e0 | ||
|
|
edb8dc58f7 | ||
|
|
3fc69f16d5 | ||
|
|
bd3fdcab26 | ||
|
|
201191e96d | ||
|
|
f545eafa77 | ||
|
|
5583c59e96 | ||
|
|
fbcf9c900c | ||
|
|
0801dea6e6 | ||
|
|
217514af66 | ||
|
|
e0191c573d | ||
|
|
ef4b53b337 | ||
|
|
acf2e4360f | ||
|
|
3227daddaf | ||
|
|
6e40b7f25b | ||
|
|
0dd87bbf78 | ||
|
|
dcfdc7d0ea | ||
|
|
e79097603f | ||
|
|
ffd8f9951f | ||
|
|
e27370cf32 | ||
|
|
405c4d1706 | ||
|
|
f7e081ba5d | ||
|
|
c71deb5051 | ||
|
|
edab722a76 | ||
|
|
2d280bd995 | ||
|
|
27e3c6553e | ||
|
|
6258a9cff9 | ||
|
|
a72f497581 | ||
|
|
311d3dd635 | ||
|
|
e80b3e4542 | ||
|
|
82c96555dc | ||
|
|
75b6e69d34 | ||
|
|
532b5f7c33 | ||
|
|
a841419c30 | ||
|
|
730a56380a | ||
|
|
97aa974443 | ||
|
|
b2eee8bde7 | ||
|
|
3cbe932248 | ||
|
|
26d5504a2f | ||
|
|
b163a0fe77 | ||
|
|
523e713d7a | ||
|
|
c7cf634a94 | ||
|
|
c8c14396f1 | ||
|
|
e72ccaf440 | ||
|
|
0b7e07ebab | ||
|
|
37e19d9a60 | ||
|
|
8f533bc576 | ||
|
|
096a9561ed | ||
|
|
c6ac9376fc | ||
|
|
fd55cf6996 | ||
|
|
0359705361 | ||
|
|
e31a7e5236 | ||
|
|
bb477908ef | ||
|
|
26175fbe1b | ||
|
|
67bc59f6b6 | ||
|
|
7b358b1bbb | ||
|
|
0387d5bdd1 | ||
|
|
2ddf624f7d | ||
|
|
74b34661a5 | ||
|
|
dc9765ef58 | ||
|
|
a50e66faf4 | ||
|
|
0388738e02 | ||
|
|
d33e38012a | ||
|
|
785d0b21c6 | ||
|
|
db12cd92b7 | ||
|
|
52261f99d7 | ||
|
|
692dc154ef | ||
|
|
22101d8f4a | ||
|
|
f68db90b1f | ||
|
|
5e680531da | ||
|
|
93cd31018e | ||
|
|
277c570723 | ||
|
|
f1419a75f6 | ||
|
|
3af08a4727 | ||
|
|
a535cea85f | ||
|
|
29e443ed76 | ||
|
|
f95cddd05a | ||
|
|
ae28de4159 | ||
|
|
39de2c1d25 | ||
|
|
22570e08aa | ||
|
|
3b4ef4d238 | ||
|
|
c5a71c4304 | ||
|
|
4f37c2cb73 | ||
|
|
c1ec97055c | ||
|
|
086c71126f | ||
|
|
10a78c1c94 | ||
|
|
cf6021d898 | ||
|
|
ff322cd2dd | ||
|
|
1a96d3c38e | ||
|
|
cfe84963fa | ||
|
|
d908a599b1 | ||
|
|
9b3ddb8da3 | ||
|
|
56d994a69d | ||
|
|
67cd17c604 | ||
|
|
f4fb7eb8b7 | ||
|
|
b9021e4331 | ||
|
|
3583836d3e | ||
|
|
278815119f | ||
|
|
1bb678e455 | ||
|
|
b9e5fc604b | ||
|
|
b65e8c48ce | ||
|
|
e59bc1a08e | ||
|
|
ff994629de | ||
|
|
a458b9bc88 | ||
|
|
2834e25151 | ||
|
|
c2582fe055 | ||
|
|
0980219c8d | ||
|
|
c5f158f1cf | ||
|
|
62afc023c8 | ||
|
|
52ca84946b | ||
|
|
e64fb39c32 | ||
|
|
1066598150 | ||
|
|
caed8c2cf0 | ||
|
|
663be2402d | ||
|
|
6fd24c842f | ||
|
|
302631c4fa | ||
|
|
8a94623b2b | ||
|
|
016dfdb455 | ||
|
|
097415385e | ||
|
|
8e6c6e04a3 | ||
|
|
81a30e580e | ||
|
|
676efe7253 | ||
|
|
39e0e77824 | ||
|
|
363f5be8ff | ||
|
|
df15fa2f0e | ||
|
|
e8c1fbb86f | ||
|
|
df986b9ecf | ||
|
|
daabf4bab9 | ||
|
|
3095948024 | ||
|
|
fad289305f | ||
|
|
97d8f07e0d | ||
|
|
dc374c7ce9 | ||
|
|
1584475bc3 | ||
|
|
8f1db25c5c | ||
|
|
092b536009 | ||
|
|
f95bbaa0f7 | ||
|
|
bc1f6464d3 | ||
|
|
b828cd5975 | ||
|
|
766b4f7971 | ||
|
|
45e2ffd71e | ||
|
|
98757c3f11 | ||
|
|
9aed6d350b | ||
|
|
ca6ce6db32 | ||
|
|
1c9a6a02af | ||
|
|
ba9bafcb5f | ||
|
|
0628394122 | ||
|
|
99b2cd2ad0 | ||
|
|
707a6ecbaa | ||
|
|
aa2bc40f03 | ||
|
|
5e00e76c4b | ||
|
|
0dba992dd4 | ||
|
|
b6f61cac9b | ||
|
|
0e41945a8a | ||
|
|
a05e037308 | ||
|
|
f40576c39d | ||
|
|
9fc810182a | ||
|
|
dd7a52ba08 | ||
|
|
fa6d2a3080 | ||
|
|
36efc0c877 | ||
|
|
e9efcf1b92 | ||
|
|
f4ad1ec8e7 | ||
|
|
0027c21630 | ||
|
|
6173cab99f | ||
|
|
80c8097a71 | ||
|
|
ba3d577125 | ||
|
|
8ce4ebb16e | ||
|
|
4358ff2338 | ||
|
|
53f9eb083d | ||
|
|
0a3a982cb9 | ||
|
|
c30239b3a8 | ||
|
|
183834689d | ||
|
|
5da2f3279b |
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
|
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps To Reproduce
|
||||||
|
description: How can we reproduce the behavior.
|
||||||
|
value: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. Click on '...'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Result
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual Result
|
||||||
|
description: A clear and concise description of what is happening.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: screenshots
|
||||||
|
attributes:
|
||||||
|
label: Screenshots or Videos
|
||||||
|
description: If applicable, add screenshots and/or a short video to help explain your problem.
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
|
- type: dropdown
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: What operating system are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: os-version
|
||||||
|
attributes:
|
||||||
|
label: Operating System Version
|
||||||
|
description: What version of the operating system(s) are you seeing the problem on?
|
||||||
|
- type: input
|
||||||
|
id: device
|
||||||
|
attributes:
|
||||||
|
label: Device
|
||||||
|
description: Which device are you seeing the problem on?
|
||||||
|
placeholder: iPhone 12, Samsung Galaxy S10
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Build Version
|
||||||
|
description: What version of our software are you running? (go to "Settings" → "About" in the app)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: beta
|
||||||
|
attributes:
|
||||||
|
label: Beta
|
||||||
|
options:
|
||||||
|
- label: Using a pre-release version of the application.
|
||||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Report mobile autofill failure
|
||||||
|
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform
|
||||||
|
about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users!
|
||||||
|
- name: Feature Requests
|
||||||
|
url: https://community.bitwarden.com/c/feature-requests/
|
||||||
|
about: Request new features using the Community Forums. Please search existing feature requests before making a new one.
|
||||||
|
- name: Bitwarden Community Forums
|
||||||
|
url: https://community.bitwarden.com
|
||||||
|
about: Please visit the community forums for general community discussion, support and the development roadmap.
|
||||||
|
- name: Customer Support
|
||||||
|
url: https://bitwarden.com/contact/
|
||||||
|
about: Please contact our customer support for account issues and general customer support.
|
||||||
|
- name: Security Issues
|
||||||
|
url: https://hackerone.com/bitwarden
|
||||||
|
about: We use HackerOne to manage security disclosures.
|
||||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
## Type of change
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] New feature development
|
||||||
|
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||||
|
- [ ] Build/deploy pipeline (DevOps)
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Code changes
|
||||||
|
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
|
||||||
|
<!--Also refer to any related changes or PRs in other repositories-->
|
||||||
|
|
||||||
|
* **file.ext:** Description of what was changed and why
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
<!--Required for any UI changes. Delete if not applicable-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Testing requirements
|
||||||
|
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Before you submit
|
||||||
|
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
||||||
|
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||||
|
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||||
17
.github/resources/export-options-ad-hoc.plist
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>method</key>
|
||||||
|
<string>ad-hoc</string>
|
||||||
|
<key>provisioningProfiles</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.8bit.bitwarden</key>
|
||||||
|
<string>Ad hoc: Bitwarden 2021</string>
|
||||||
|
<key>com.8bit.bitwarden.autofill</key>
|
||||||
|
<string>Ad hoc: Autofill 2021</string>
|
||||||
|
<key>com.8bit.bitwarden.find-login-action-extension</key>
|
||||||
|
<string>Ad hoc: Extension 2021</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
17
.github/resources/export-options-app-store.plist
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>method</key>
|
||||||
|
<string>app-store</string>
|
||||||
|
<key>provisioningProfiles</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.8bit.bitwarden</key>
|
||||||
|
<string>Dist: Bitwarden 2021</string>
|
||||||
|
<key>com.8bit.bitwarden.autofill</key>
|
||||||
|
<string>Dist: Autofill 2021</string>
|
||||||
|
<key>com.8bit.bitwarden.find-login-action-extension</key>
|
||||||
|
<string>Dist: Extension 2021</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
Normal file
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
Normal file
3
.github/secrets/google-services.json.gpg
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<EFBFBD>
|
||||||
|
K<>Y#<23>(<28><><EFBFBD><EFBFBD>EI߄T?)l<><6C><EFBFBD><18><><10>"=<3D>|<7C>'e<><0E>m<EFBFBD>/~<7E><>'F<><46>><3E><><EFBFBD><EFBFBD>l<EFBFBD>b<EFBFBD>[<5B>+R<><52>iL<69><4C>"<22><><EFBFBD>~V:<3A><>p<EFBFBD>a<17>ڵel%8t<38><74>튖<EFBFBD>y<<3C>n<EFBFBD><6E><EFBFBD>aU<61>w<16>JD<4A><44><1F><>We<57>9<EFBFBD><39><EFBFBD><EFBFBD><x8d<38>O<EFBFBD>j\<14>ד<EFBFBD><D793><EFBFBD>Vq<56><71>
|
||||||
|
Ǻ<EFBFBD>-<2D>#<23><><11><>]$<24>(<28>l,<2C>Br<42><02><>d<><64><EFBFBD>a-<2D><><EFBFBD>:<3A><>:<3A><04>9b,!Em<02><19><>Qf<>D<EFBFBD>g<EFBFBD><06><0E>x(P<>ȡ~<7E><EFBFBD><CDB9> <09><>[<06><>!:<3A>;f<><66>
|
||||||
BIN
.github/secrets/iphone-distribution-cert.p12.gpg
vendored
Normal file
BIN
.github/secrets/play_creds.json.gpg
vendored
Normal file
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
Normal file
528
.github/workflows/build.yml
vendored
@@ -1,51 +1,537 @@
|
|||||||
|
---
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on: push
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'l10n_master'
|
||||||
|
- 'gh-pages'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
cloc:
|
||||||
|
name: CLOC
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
|
||||||
|
setup:
|
||||||
|
name: Setup
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
outputs:
|
||||||
|
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
|
||||||
|
release_branch_exists: ${{ steps.branch-check.outputs.release_branch_exists }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Check if special branches exist
|
||||||
|
id: branch-check
|
||||||
|
run: |
|
||||||
|
if [[ $(git ls-remote --heads origin rc) ]]; then
|
||||||
|
echo "::set-output name=rc_branch_exists::1"
|
||||||
|
else
|
||||||
|
echo "::set-output name=rc_branch_exists::0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $(git ls-remote --heads origin release) ]]; then
|
||||||
|
echo "::set-output name=release_branch_exists::1"
|
||||||
|
else
|
||||||
|
echo "::set-output name=release_branch_exists::0"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
android:
|
android:
|
||||||
|
name: Android
|
||||||
runs-on: windows-latest
|
runs-on: windows-2019
|
||||||
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Set up NuGet
|
|
||||||
uses: nuget/setup-nuget@v1
|
|
||||||
with:
|
|
||||||
nuget-version: 'latest'
|
|
||||||
|
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@v1.0.0
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help
|
nuget help | grep Version
|
||||||
msbuild -version
|
msbuild -version
|
||||||
|
dotnet --info
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Increment version
|
||||||
|
run: |
|
||||||
|
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
|
./src/Android/Properties/AndroidManifest.xml
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
- name: Build app
|
- name: Run Core tests
|
||||||
run: msbuild ./src/Android/Android.csproj /p:Configuration=Debug
|
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||||
|
|
||||||
|
- name: Build Play Store publisher
|
||||||
|
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
|
||||||
|
|
||||||
|
- name: Build for Play Store
|
||||||
|
run: |
|
||||||
|
$configuration = "Release";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Sign for Play Store
|
||||||
|
env:
|
||||||
|
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
|
||||||
|
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy Google Play Bundle to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
|
||||||
|
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.aab");
|
||||||
|
|
||||||
|
Copy-Item $signedAabPath $signedAabDestPath
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign APK Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy Release APK to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
|
||||||
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.apk");
|
||||||
|
|
||||||
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Upload Play Store .aab artifact
|
||||||
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
|
with:
|
||||||
|
name: com.x8bit.bitwarden.aab
|
||||||
|
path: ./com.x8bit.bitwarden.aab
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload Play Store .apk artifact
|
||||||
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
|
with:
|
||||||
|
name: com.x8bit.bitwarden.apk
|
||||||
|
path: ./com.x8bit.bitwarden.apk
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Deploy to Play Store
|
||||||
|
if: |
|
||||||
|
(github.ref == 'refs/heads/master'
|
||||||
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
|
&& needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| github.ref == 'refs/heads/release'
|
||||||
|
run: |
|
||||||
|
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll"
|
||||||
|
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||||
|
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
|
||||||
|
TRACK="internal"
|
||||||
|
|
||||||
|
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
f-droid:
|
||||||
|
name: F-Droid Build
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- name: Set up MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
||||||
|
|
||||||
|
- name: Print environment
|
||||||
|
run: |
|
||||||
|
nuget help | grep Version
|
||||||
|
msbuild -version
|
||||||
|
dotnet --info
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Increment version
|
||||||
|
run: |
|
||||||
|
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
|
./src/Android/Properties/AndroidManifest.xml
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Clean for F-Droid
|
||||||
|
run: |
|
||||||
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
|
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
||||||
|
|
||||||
|
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Clean Android and App"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||||
|
msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Backup project files"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
Copy-Item $androidManifest $($androidManifest + ".original");
|
||||||
|
Copy-Item $androidPath $($androidPath + ".original");
|
||||||
|
Copy-Item $appPath $($appPath + ".original");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Cleanup Android Manifest"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($androidManifest);
|
||||||
|
|
||||||
|
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||||
|
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
||||||
|
|
||||||
|
$xml.Save($androidManifest);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from Android.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($androidPath);
|
||||||
|
|
||||||
|
$ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||||
|
$ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
|
||||||
|
|
||||||
|
$firebaseNode=$xml.SelectSingleNode(`
|
||||||
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
||||||
|
$firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
||||||
|
|
||||||
|
$daggerNode=$xml.SelectSingleNode(`
|
||||||
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
|
||||||
|
$daggerNode.ParentNode.RemoveChild($daggerNode);
|
||||||
|
|
||||||
|
$safetyNetNode=$xml.SelectSingleNode(`
|
||||||
|
"/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
||||||
|
$safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
||||||
|
|
||||||
|
$xml.Save($androidPath);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from App.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($appPath);
|
||||||
|
|
||||||
|
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
|
$xml.Save($appPath);
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Restore packages
|
||||||
|
run: nuget restore
|
||||||
|
|
||||||
|
- name: Build for F-Droid
|
||||||
|
run: |
|
||||||
|
$configuration = "FDroid";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Sign for F-Droid
|
||||||
|
env:
|
||||||
|
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign FDroid Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" `
|
||||||
|
"/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy FDroid apk to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
|
||||||
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
|
||||||
|
|
||||||
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Upload F-Droid .apk artifact
|
||||||
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
|
with:
|
||||||
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
path: ./com.x8bit.bitwarden-fdroid.apk
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
ios:
|
ios:
|
||||||
|
name: Apple iOS
|
||||||
runs-on: macos-latest
|
runs-on: macos-11
|
||||||
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help
|
nuget help | grep Version
|
||||||
msbuild -version
|
msbuild -version
|
||||||
|
dotnet --info
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Increment version
|
||||||
|
run: |
|
||||||
|
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set up Keychain
|
||||||
|
env:
|
||||||
|
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||||
|
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
|
||||||
|
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
security set-keychain-settings -lut 1200 build.keychain
|
||||||
|
security import ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
|
||||||
|
-T /usr/bin/codesign -T /usr/bin/security
|
||||||
|
security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
|
||||||
|
-T /usr/bin/codesign -T /usr/bin/security
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set up provisioning profiles
|
||||||
|
run: |
|
||||||
|
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_autofill.mobileprovision
|
||||||
|
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
|
||||||
|
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
|
||||||
|
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
|
||||||
|
mkdir -p "$PROFILES_DIR_PATH"
|
||||||
|
|
||||||
|
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"
|
||||||
|
|
||||||
|
BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"
|
||||||
|
|
||||||
|
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
- name: Build app
|
- name: Archive Build for App Store
|
||||||
run: msbuild ./src/iOS/iOS.csproj /verbosity:normal /p:Platform=iPhone /p:Configuration=Debug
|
run: |
|
||||||
|
$configuration = "AppStore";
|
||||||
|
$platform = "iPhone";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Archive $configuration Configuration for $platform Platform"
|
||||||
|
Write-Output "########################################"
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
|
||||||
|
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Done"
|
||||||
|
Write-Output "########################################"
|
||||||
|
ls ~/Library/Developer/Xcode/Archives
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Export .ipa for App Store
|
||||||
|
run: |
|
||||||
|
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
|
||||||
|
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
|
||||||
|
EXPORT_PATH="./bitwarden-export"
|
||||||
|
|
||||||
|
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
|
||||||
|
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload App Store .ipa artifact
|
||||||
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
|
with:
|
||||||
|
name: Bitwarden.ipa
|
||||||
|
path: ./bitwarden-export/Bitwarden.ipa
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Deploy to App Store
|
||||||
|
if: |
|
||||||
|
(github.ref == 'refs/heads/master'
|
||||||
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
|
&& needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| github.ref == 'refs/heads/release'
|
||||||
|
env:
|
||||||
|
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||||
|
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
|
||||||
|
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
check-failures:
|
||||||
|
name: Check for failures
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs:
|
||||||
|
- cloc
|
||||||
|
- android
|
||||||
|
- f-droid
|
||||||
|
- ios
|
||||||
|
steps:
|
||||||
|
- name: Check if any job failed
|
||||||
|
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
|
||||||
|
env:
|
||||||
|
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||||
|
ANDROID_STATUS: ${{ needs.android.result }}
|
||||||
|
F_DROID_STATUS: ${{ needs.f-droid.result }}
|
||||||
|
IOS_STATUS: ${{ needs.ios.result }}
|
||||||
|
run: |
|
||||||
|
if [ "$CLOC_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$ANDROID_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$F_DROID_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$IOS_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Login to Azure - Prod Subscription
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Retrieve secrets
|
||||||
|
id: retrieve-secrets
|
||||||
|
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "devops-alerts-slack-webhook-url"
|
||||||
|
|
||||||
|
- name: Notify Slack on failure
|
||||||
|
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
|
||||||
|
if: failure()
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||||
|
with:
|
||||||
|
status: ${{ job.status }}
|
||||||
|
|||||||
49
.github/workflows/crowdin-sync.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: Crowdin Sync
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
# schedule:
|
||||||
|
# - cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
crowdin-sync:
|
||||||
|
name: Autosync
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- 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: Download translations
|
||||||
|
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
|
with:
|
||||||
|
config: crowdin.yml
|
||||||
|
crowdin_branch_name: master
|
||||||
|
upload_sources: false
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: true
|
||||||
|
github_user_name: "github-actions"
|
||||||
|
github_user_email: "<>"
|
||||||
|
commit_message: "Autosync the updated translations"
|
||||||
|
localization_branch_name: crowdin-auto-sync
|
||||||
|
create_pull_request: true
|
||||||
|
pull_request_title: "Autosync Crowdin Translations"
|
||||||
|
pull_request_body: "Autosync the updated translations"
|
||||||
153
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Branch check
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_REF" != "refs/heads/release" ]]; then
|
||||||
|
echo "==================================="
|
||||||
|
echo "[!] Can only release from the 'release' branch"
|
||||||
|
echo "==================================="
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
with:
|
||||||
|
ref: release
|
||||||
|
|
||||||
|
- name: Retrieve Mobile release version
|
||||||
|
id: retrieve-mobile-version
|
||||||
|
run: |
|
||||||
|
ver=$(sed -n -e '/android:versionName/ s/.*\= *//p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
||||||
|
echo "::set-output name=mobile_version::${ver}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Check to make sure Mobile release version has been bumped
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
latest_ver=$(hub release -L 1 -f '%T')
|
||||||
|
latest_ver=${latest_ver:1}
|
||||||
|
echo "Latest version: $latest_ver"
|
||||||
|
ver=${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
echo "Version: $ver"
|
||||||
|
if [ "$latest_ver" = "$ver" ]; then
|
||||||
|
echo "Version has not been bumped!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
|
with:
|
||||||
|
workflow: build.yml
|
||||||
|
workflow_conclusion: success
|
||||||
|
branch: release
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||||
|
with:
|
||||||
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
|
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||||
|
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||||
|
./Bitwarden.ipa/Bitwarden.ipa"
|
||||||
|
commit: ${{ github.sha }}
|
||||||
|
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
body: "<insert release notes here>"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
draft: true
|
||||||
|
|
||||||
|
|
||||||
|
f-droid:
|
||||||
|
name: F-Droid Release
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: release
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
with:
|
||||||
|
ref: release
|
||||||
|
|
||||||
|
- name: Download F-Droid .apk artifact
|
||||||
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
|
with:
|
||||||
|
workflow: build.yml
|
||||||
|
workflow_conclusion: success
|
||||||
|
branch: release
|
||||||
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.3.0
|
||||||
|
with:
|
||||||
|
node-version: '10.x'
|
||||||
|
|
||||||
|
- name: Set up F-Droid server
|
||||||
|
run: |
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
||||||
|
|
||||||
|
- name: Set up Git credentials
|
||||||
|
env:
|
||||||
|
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
run: |
|
||||||
|
git config --global credential.helper store
|
||||||
|
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
|
||||||
|
git config --global user.email "ci@bitwarden.com"
|
||||||
|
git config --global user.name "Bitwarden CI"
|
||||||
|
|
||||||
|
- name: Print environment
|
||||||
|
run: |
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
git --version
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
|
||||||
|
|
||||||
|
- name: Compile for F-Droid Store
|
||||||
|
env:
|
||||||
|
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
mkdir dist
|
||||||
|
cp CNAME ./dist
|
||||||
|
cd store
|
||||||
|
chmod 600 fdroid/config.py fdroid/keystore.jks
|
||||||
|
mkdir -p temp/fdroid
|
||||||
|
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
||||||
|
cd fdroid
|
||||||
|
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||||
|
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||||
|
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
||||||
|
mkdir -p repo
|
||||||
|
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
|
||||||
|
fdroid update
|
||||||
|
fdroid server update
|
||||||
|
cd ..
|
||||||
|
rm -rf temp/fdroid/archive
|
||||||
|
mv -v temp/fdroid ../dist
|
||||||
|
cd fdroid
|
||||||
|
cp index.html btn.png qr.png ../../dist/fdroid
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
|
||||||
|
- name: Deploy to gh-pages
|
||||||
|
run: npm run deploy
|
||||||
@@ -1,4 +1,31 @@
|
|||||||
Code contributions are welcome! Please commit any pull requests against the `master` branch.
|
# How to Contribute
|
||||||
|
|
||||||
|
Contributions of all kinds are welcome!
|
||||||
|
|
||||||
|
Please visit our [Community Forums](https://community.bitwarden.com/) for general community discussion and the development roadmap.
|
||||||
|
|
||||||
|
Here is how you can get involved:
|
||||||
|
|
||||||
|
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||||
|
|
||||||
|
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||||
|
|
||||||
|
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||||
|
|
||||||
|
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||||
|
|
||||||
|
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||||
|
|
||||||
|
* **Translate:** See the localization (i10n) section below
|
||||||
|
|
||||||
|
## Contributor Agreement
|
||||||
|
|
||||||
|
Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/mobile) if you intend on contributing to any Github repository. Pull requests cannot be accepted and merged unless the author has signed the Contributor Agreement.
|
||||||
|
|
||||||
|
## Pull Request Guidelines
|
||||||
|
|
||||||
|
* commit any pull requests against the `master` branch
|
||||||
|
* include a link to your Community Forums post
|
||||||
|
|
||||||
# Localization (l10n)
|
# Localization (l10n)
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
<!-- Comment:
|
|
||||||
Please do not submit feature requests. The [Community Forums][1] has a
|
|
||||||
section for submitting, voting for, and discussing product feature requests.
|
|
||||||
[1]: https://community.bitwarden.com
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Describe the Bug
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Steps To Reproduce
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
How can we reproduce the behavior:
|
|
||||||
-->
|
|
||||||
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. Click on '...'
|
|
||||||
|
|
||||||
## Expected Result
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Actual Result
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what is happening.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Screenshots or Videos
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
If applicable, add screenshots and/or a short video to help explain your problem.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- Operating system: [e.g. iOS 8.1]
|
|
||||||
- Build Version (go to "Settings" → "About" in the app): [e.g. 2.3.0 (2221)]
|
|
||||||
- Is this a Beta release? [Y/N]
|
|
||||||
|
|
||||||
## Additional Context
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
Add any other context about the problem here.
|
|
||||||
-->
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://ci.appveyor.com/project/bitwarden/mobile)
|
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
|
||||||
[](https://crowdin.com/project/bitwarden-mobile)
|
[](https://crowdin.com/project/bitwarden-mobile)
|
||||||
[](https://gitter.im/bitwarden/Lobby)
|
[](https://gitter.im/bitwarden/Lobby)
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="300" height="533" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="533" />
|
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
|
||||||
|
|
||||||
# Build/Run
|
# Build/Run
|
||||||
|
|
||||||
|
|||||||
146
appveyor.yml
@@ -1,146 +0,0 @@
|
|||||||
image:
|
|
||||||
- Visual Studio 2019
|
|
||||||
- Ubuntu1804
|
|
||||||
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- l10n_master
|
|
||||||
- gh-pages
|
|
||||||
|
|
||||||
configuration: Release
|
|
||||||
|
|
||||||
stack: node 10
|
|
||||||
|
|
||||||
init:
|
|
||||||
- sh: |
|
|
||||||
if [ "${DEBUG_SSH}" == "true" ]
|
|
||||||
then
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:DEBUG_RDP -eq "true") {
|
|
||||||
iex ((new-object net.webclient).DownloadString(`
|
|
||||||
'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
|
||||||
}
|
|
||||||
- ps: |
|
|
||||||
if($env:APPVEYOR_REPO_TAG -eq "true") {
|
|
||||||
$tagName = $env:APPVEYOR_REPO_TAG_NAME.TrimStart("v")
|
|
||||||
$env:RELEASE_NAME = "Version ${tagName}"
|
|
||||||
}
|
|
||||||
|
|
||||||
install:
|
|
||||||
- sh: |
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/secure-file/master/install.sh' | bash -e -
|
|
||||||
./appveyor-tools/secure-file -decrypt ./store/fdroid/keystore.jks.enc -secret $FDROID_KEYSTORE_ENC_PASSWORD
|
|
||||||
- sh: npm install
|
|
||||||
- sh: |
|
|
||||||
sudo apt-get -qq update
|
|
||||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" -a "${GH_TOKEN}" != "" ]
|
|
||||||
then
|
|
||||||
git config --global credential.helper store
|
|
||||||
echo "https://${GH_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
|
|
||||||
git config --global user.email "ci@bitwarden.com"
|
|
||||||
git config --global user.name "Bitwarden CI"
|
|
||||||
fi
|
|
||||||
- cmd: choco install cloc --no-progress
|
|
||||||
- cmd: "cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML"
|
|
||||||
#- cmd: appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
|
||||||
#- cmd: appveyor DownloadFile https://aka.ms/vs/15/release/vs_community.exe
|
|
||||||
#- cmd: vs_community.exe update --wait --quiet --norestart --installPath "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community"
|
|
||||||
#- cmd: ps: .\src\Android\update-android.ps1
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- ps: |
|
|
||||||
if($isWindows) {
|
|
||||||
nuget restore
|
|
||||||
if($env:UPLOAD_KEYSTORE_DEC_SECRET -or$env:KEYSTORE_DEC_SECRET -or $env:GOOGLE_SERVICES_DEC_SECRET -or $env:PLAY_DEC_SECRET) {
|
|
||||||
nuget install secure-file -ExcludeVersion
|
|
||||||
}
|
|
||||||
if($env:GOOGLE_SERVICES_DEC_SECRET) {
|
|
||||||
secure-file\tools\secure-file -decrypt src\Android\google-services.json.enc `
|
|
||||||
-secret $env:GOOGLE_SERVICES_DEC_SECRET
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" ]
|
|
||||||
then
|
|
||||||
mkdir dist
|
|
||||||
cp CNAME ./dist
|
|
||||||
cd store
|
|
||||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
|
||||||
mkdir -p temp/fdroid
|
|
||||||
TEMP_DIR="$APPVEYOR_BUILD_FOLDER/store/temp/fdroid"
|
|
||||||
cd fdroid
|
|
||||||
echo "keypass=\"$FDROID_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "keystorepass=\"$FDROID_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
|
||||||
mkdir -p repo
|
|
||||||
curl -Lo repo/com.x8bit.bitwarden-fdroid.apk \
|
|
||||||
https://github.com/bitwarden/mobile/releases/download/$APPVEYOR_REPO_TAG_NAME/com.x8bit.bitwarden-fdroid.apk
|
|
||||||
fdroid update
|
|
||||||
fdroid server update
|
|
||||||
cd ..
|
|
||||||
rm -rf temp/fdroid/archive
|
|
||||||
mv -v temp/fdroid ../dist
|
|
||||||
cd fdroid
|
|
||||||
cp index.html btn.png qr.png ../../dist/fdroid
|
|
||||||
cd $APPVEYOR_BUILD_FOLDER
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:KEYSTORE_DEC_SECRET) {
|
|
||||||
msbuild bitwarden-mobile.sln `
|
|
||||||
"/logger:C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" `
|
|
||||||
"/p:Configuration=Release"
|
|
||||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
|
||||||
.\src\Android\ci-build-apks.ps1
|
|
||||||
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.aab
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden.apk
|
|
||||||
Push-AppveyorArtifact .\com.x8bit.bitwarden-fdroid.apk
|
|
||||||
}
|
|
||||||
|
|
||||||
on_success:
|
|
||||||
- sh: |
|
|
||||||
if [ "${APPVEYOR_REPO_TAG}" == "true" -a "${GH_TOKEN}" != "" ]
|
|
||||||
then
|
|
||||||
npm run deploy
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:PLAY_DEC_SECRET -and $env:APPVEYOR_REPO_BRANCH -eq 'master') {
|
|
||||||
secure-file\tools\secure-file -decrypt store\google\Publisher\play_creds.json.enc -secret $env:PLAY_DEC_SECRET
|
|
||||||
cd store\google\Publisher\bin\Release\netcoreapp2.0
|
|
||||||
dotnet Publisher.dll `
|
|
||||||
$env:APPVEYOR_BUILD_FOLDER\store\google\Publisher\play_creds.json `
|
|
||||||
$env:APPVEYOR_BUILD_FOLDER\com.x8bit.bitwarden.aab `
|
|
||||||
alpha
|
|
||||||
cd $env:APPVEYOR_BUILD_FOLDER
|
|
||||||
}
|
|
||||||
|
|
||||||
on_finish:
|
|
||||||
- sh: |
|
|
||||||
if [ "${DEBUG_SSH}" == "true" ]
|
|
||||||
then
|
|
||||||
export APPVEYOR_SSH_BLOCK=true
|
|
||||||
curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
|
|
||||||
fi
|
|
||||||
- ps: |
|
|
||||||
if($isWindows -and $env:DEBUG_RDP -eq "true") {
|
|
||||||
$blockRdp = $true
|
|
||||||
iex ((new-object net.webclient).DownloadString(`
|
|
||||||
'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
|
||||||
}
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
tag: $(APPVEYOR_REPO_TAG_NAME)
|
|
||||||
release: $(RELEASE_NAME)
|
|
||||||
provider: GitHub
|
|
||||||
auth_token: $(GH_TOKEN)
|
|
||||||
artifact: /.*/
|
|
||||||
force_update: true
|
|
||||||
on:
|
|
||||||
branch: master
|
|
||||||
APPVEYOR_REPO_TAG: true
|
|
||||||
@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
.gitignore = .gitignore
|
.gitignore = .gitignore
|
||||||
appveyor.yml = appveyor.yml
|
|
||||||
.github\workflows\build.yml = .github\workflows\build.yml
|
.github\workflows\build.yml = .github\workflows\build.yml
|
||||||
CONTRIBUTING.md = CONTRIBUTING.md
|
CONTRIBUTING.md = CONTRIBUTING.md
|
||||||
crowdin.yml = crowdin.yml
|
crowdin.yml = crowdin.yml
|
||||||
@@ -41,6 +40,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Ex
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Safari", "src\iOS.Safari\iOS.Safari.csproj", "{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||||
@@ -351,6 +356,96 @@ Global
|
|||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.Build.0 = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -366,6 +461,9 @@ Global
|
|||||||
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
project_id_env: _CROWDIN_PROJECT_ID
|
||||||
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
files:
|
files:
|
||||||
- source: /src/App/Resources/AppResources.resx
|
- source: /src/App/Resources/AppResources.resx
|
||||||
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
||||||
@@ -9,6 +11,7 @@ files:
|
|||||||
pt-PT: pt-PT
|
pt-PT: pt-PT
|
||||||
pt-BR: pt-BR
|
pt-BR: pt-BR
|
||||||
en-GB: en-GB
|
en-GB: en-GB
|
||||||
|
en-IN: en-IN
|
||||||
- source: /store/apple/en/copy.resx
|
- source: /store/apple/en/copy.resx
|
||||||
translation: /store/apple/%two_letters_code%/copy.resx
|
translation: /store/apple/%two_letters_code%/copy.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
@@ -19,6 +22,7 @@ files:
|
|||||||
pt-PT: pt-PT
|
pt-PT: pt-PT
|
||||||
pt-BR: pt-BR
|
pt-BR: pt-BR
|
||||||
en-GB: en-GB
|
en-GB: en-GB
|
||||||
|
en-IN: en-IN
|
||||||
- source: /store/google/en/copy.resx
|
- source: /store/google/en/copy.resx
|
||||||
translation: /store/google/%two_letters_code%/copy.resx
|
translation: /store/google/%two_letters_code%/copy.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
@@ -29,3 +33,4 @@ files:
|
|||||||
pt-BR: pt-BR
|
pt-BR: pt-BR
|
||||||
pt-PT: pt-PT
|
pt-PT: pt-PT
|
||||||
en-GB: en-GB
|
en-GB: en-GB
|
||||||
|
en-IN: en-IN
|
||||||
|
|||||||
710
package-lock.json
generated
@@ -1,8 +1,468 @@
|
|||||||
{
|
{
|
||||||
"name": "bitwarden-mobile",
|
"name": "bitwarden-mobile",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "bitwarden-mobile",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"gh-pages": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-union": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"array-uniq": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-uniq": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/email-addresses": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filename-reserved-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filenamify": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"filename-reserved-regex": "^2.0.0",
|
||||||
|
"strip-outer": "^1.0.1",
|
||||||
|
"trim-repeated": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-cache-dir": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"make-dir": "^3.0.2",
|
||||||
|
"pkg-dir": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"universalify": "^0.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6 <7 || >=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/gh-pages": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^2.6.1",
|
||||||
|
"commander": "^2.18.0",
|
||||||
|
"email-addresses": "^3.0.1",
|
||||||
|
"filenamify": "^4.3.0",
|
||||||
|
"find-cache-dir": "^3.3.1",
|
||||||
|
"fs-extra": "^8.1.0",
|
||||||
|
"globby": "^6.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"gh-pages": "bin/gh-pages.js",
|
||||||
|
"gh-pages-clean": "bin/gh-pages-clean.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/globby": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||||
|
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"array-union": "^1.0.1",
|
||||||
|
"glob": "^7.0.3",
|
||||||
|
"object-assign": "^4.0.1",
|
||||||
|
"pify": "^2.0.0",
|
||||||
|
"pinkie-promise": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"dev": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pify": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinkie": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||||
|
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinkie-promise": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pinkie": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pkg-dir": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"find-up": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-outer": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/trim-repeated": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-union": {
|
"array-union": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -20,18 +480,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
|
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.10"
|
"lodash": "^4.17.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
@@ -45,9 +505,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.15.1",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
@@ -56,6 +522,12 @@
|
|||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"email-addresses": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
@@ -63,39 +535,50 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"filename-reserved-regex": {
|
"filename-reserved-regex": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||||
"integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=",
|
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"filenamify": {
|
"filenamify": {
|
||||||
"version": "1.2.1",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
|
||||||
"integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=",
|
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"filename-reserved-regex": "^1.0.0",
|
"filename-reserved-regex": "^2.0.0",
|
||||||
"strip-outer": "^1.0.0",
|
"strip-outer": "^1.0.1",
|
||||||
"trim-repeated": "^1.0.0"
|
"trim-repeated": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filenamify-url": {
|
"find-cache-dir": {
|
||||||
"version": "1.0.0",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||||
"integrity": "sha1-syvYExnvWGO3MHi+1Q9GpPeXX1A=",
|
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"filenamify": "^1.0.0",
|
"commondir": "^1.0.1",
|
||||||
"humanize-url": "^1.0.0"
|
"make-dir": "^3.0.2",
|
||||||
|
"pkg-dir": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "5.0.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
}
|
}
|
||||||
@@ -107,24 +590,24 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"gh-pages": {
|
"gh-pages": {
|
||||||
"version": "1.2.0",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
||||||
"integrity": "sha512-cGLYAvxtlQ1iTwAS4g7FreZPXoE/g62Fsxln2mmR19mgs4zZI+XJ+wVVUhBFCF/0+Nmvbq+abyTWue1m1BSnmg==",
|
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "2.6.1",
|
"async": "^2.6.1",
|
||||||
"commander": "2.15.1",
|
"commander": "^2.18.0",
|
||||||
"filenamify-url": "^1.0.0",
|
"email-addresses": "^3.0.1",
|
||||||
"fs-extra": "^5.0.0",
|
"filenamify": "^4.3.0",
|
||||||
"globby": "^6.1.0",
|
"find-cache-dir": "^3.3.1",
|
||||||
"graceful-fs": "4.1.11",
|
"fs-extra": "^8.1.0",
|
||||||
"rimraf": "^2.6.2"
|
"globby": "^6.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.4",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
@@ -149,21 +632,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.1.11",
|
"version": "4.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"humanize-url": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"normalize-url": "^1.0.0",
|
|
||||||
"strip-url-auth": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
@@ -175,15 +648,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-plain-obj": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
@@ -195,12 +662,30 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@@ -210,18 +695,6 @@
|
|||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"normalize-url": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
|
|
||||||
"integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"object-assign": "^4.0.1",
|
|
||||||
"prepend-http": "^1.0.0",
|
|
||||||
"query-string": "^4.1.0",
|
|
||||||
"sort-keys": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -237,6 +710,36 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -264,44 +767,19 @@
|
|||||||
"pinkie": "^2.0.0"
|
"pinkie": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prepend-http": {
|
"pkg-dir": {
|
||||||
"version": "1.0.4",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"query-string": {
|
|
||||||
"version": "4.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
|
||||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"object-assign": "^4.1.0",
|
"find-up": "^4.0.0"
|
||||||
"strict-uri-encode": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rimraf": {
|
"semver": {
|
||||||
"version": "2.6.3",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sort-keys": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"is-plain-obj": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strict-uri-encode": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"strip-outer": {
|
"strip-outer": {
|
||||||
@@ -313,12 +791,6 @@
|
|||||||
"escape-string-regexp": "^1.0.2"
|
"escape-string-regexp": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-url-auth": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"trim-repeated": {
|
"trim-repeated": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
"clean:l10n": "git push origin --delete l10n_master"
|
"clean:l10n": "git push origin --delete l10n_master"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "^1.2.0"
|
"gh-pages": "^3.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
namespace Bit.Droid.Accessibility
|
namespace Bit.Droid.Accessibility
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@style/LightTheme.Splash", WindowSoftInputMode = SoftInput.StateHidden)]
|
[Activity(Theme = "@style/BaseTheme", WindowSoftInputMode = SoftInput.StateHidden)]
|
||||||
public class AccessibilityActivity : Activity
|
public class AccessibilityActivity : Activity
|
||||||
{
|
{
|
||||||
private DateTime? _lastLaunch = null;
|
private DateTime? _lastLaunch = null;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.amazon.cloud9", "url"),
|
new Browser("com.amazon.cloud9", "url"),
|
||||||
new Browser("com.android.browser", "url"),
|
new Browser("com.android.browser", "url"),
|
||||||
new Browser("com.android.chrome", "url_bar"),
|
new Browser("com.android.chrome", "url_bar"),
|
||||||
|
// Rem. for "com.android.htmlviewer": doesn't have a URL bar, therefore not present here.
|
||||||
new Browser("com.avast.android.secure.browser", "editor"),
|
new Browser("com.avast.android.secure.browser", "editor"),
|
||||||
new Browser("com.avg.android.secure.browser", "editor"),
|
new Browser("com.avg.android.secure.browser", "editor"),
|
||||||
new Browser("com.brave.browser", "url_bar"),
|
new Browser("com.brave.browser", "url_bar"),
|
||||||
@@ -44,19 +45,29 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.chrome.beta", "url_bar"),
|
new Browser("com.chrome.beta", "url_bar"),
|
||||||
new Browser("com.chrome.canary", "url_bar"),
|
new Browser("com.chrome.canary", "url_bar"),
|
||||||
new Browser("com.chrome.dev", "url_bar"),
|
new Browser("com.chrome.dev", "url_bar"),
|
||||||
|
new Browser("com.cookiegames.smartcookie", "search"),
|
||||||
|
new Browser("com.cookiejarapps.android.smartcookieweb", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
new Browser("com.duckduckgo.mobile.android", "omnibarTextInput"),
|
||||||
new Browser("com.ecosia.android", "url_bar"),
|
new Browser("com.ecosia.android", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||||
|
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
|
||||||
|
new Browser("com.jamal2367.styx", "search"),
|
||||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx", "url_bar"),
|
new Browser("com.microsoft.emmx", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.beta", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.canary", "url_bar"),
|
||||||
|
new Browser("com.microsoft.emmx.dev", "url_bar"),
|
||||||
|
new Browser("com.mmbox.browser", "search_box"),
|
||||||
|
new Browser("com.mmbox.xbrowser", "search_box"),
|
||||||
|
new Browser("com.mycompany.app.soulbrowser", "edit_text"),
|
||||||
new Browser("com.naver.whale", "url_bar"),
|
new Browser("com.naver.whale", "url_bar"),
|
||||||
new Browser("com.opera.browser", "url_field"),
|
new Browser("com.opera.browser", "url_field"),
|
||||||
new Browser("com.opera.browser.beta", "url_field"),
|
new Browser("com.opera.browser.beta", "url_field"),
|
||||||
new Browser("com.opera.mini.native", "url_field"),
|
new Browser("com.opera.mini.native", "url_field"),
|
||||||
new Browser("com.opera.mini.native.beta", "url_field"),
|
new Browser("com.opera.mini.native.beta", "url_field"),
|
||||||
new Browser("com.opera.touch", "addressbarEdit"),
|
new Browser("com.opera.touch", "addressbarEdit"),
|
||||||
new Browser("com.qwant.liberty", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
|
||||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||||
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
|
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
|
||||||
@@ -66,7 +77,17 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
|
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
|
||||||
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
|
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
|
||||||
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
|
||||||
new Browser("mark.via.gp", "aw"),
|
new Browser("com.z28j.feel", "g2"),
|
||||||
|
new Browser("idm.internet.download.manager", "search"),
|
||||||
|
new Browser("idm.internet.download.manager.adm.lite", "search"),
|
||||||
|
new Browser("idm.internet.download.manager.plus", "search"),
|
||||||
|
new Browser("io.github.forkmaintainers.iceraven", "mozac_browser_toolbar_url_view"),
|
||||||
|
new Browser("mark.via", "am,an"),
|
||||||
|
new Browser("mark.via.gp", "as"),
|
||||||
|
new Browser("net.slions.fulguris.full.download", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.download.debug", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.playstore", "search"),
|
||||||
|
new Browser("net.slions.fulguris.full.playstore.debug", "search"),
|
||||||
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
new Browser("org.adblockplus.browser", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
new Browser("org.adblockplus.browser.beta", "url_bar,url_bar_title"), // 2nd = Legacy (before v2)
|
||||||
new Browser("org.bromite.bromite", "url_bar"),
|
new Browser("org.bromite.bromite", "url_bar"),
|
||||||
@@ -75,17 +96,20 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
new Browser("org.codeaurora.swe.browser", "url_bar"),
|
||||||
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
new Browser("org.gnu.icecat", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
||||||
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
|
new Browser("org.mozilla.fenix", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"),
|
new Browser("org.mozilla.fenix.nightly", "mozac_browser_toolbar_url_view"), // [DEPRECATED ENTRY]
|
||||||
new Browser("org.mozilla.fennec_aurora", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
new Browser("org.mozilla.fennec_aurora", "mozac_browser_toolbar_url_view,url_bar_title"), // [DEPRECATED ENTRY]
|
||||||
new Browser("org.mozilla.fennec_fdroid", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
new Browser("org.mozilla.fennec_fdroid", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.firefox", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
new Browser("org.mozilla.firefox", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.focus", "display_url"),
|
new Browser("org.mozilla.focus", "display_url"),
|
||||||
new Browser("org.mozilla.klar", "display_url"),
|
new Browser("org.mozilla.klar", "display_url"),
|
||||||
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("org.mozilla.rocket", "display_url"),
|
new Browser("org.mozilla.rocket", "display_url"),
|
||||||
new Browser("org.torproject.torbrowser", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
new Browser("org.torproject.torbrowser", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0.3)
|
||||||
new Browser("org.torproject.torbrowser_alpha", "url_bar_title,mozac_browser_toolbar_url_view"), // 2nd = Anticipation
|
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.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
|
||||||
//
|
//
|
||||||
@@ -128,16 +152,199 @@ namespace Bit.Droid.Accessibility
|
|||||||
"com.ss.squarehome2",
|
"com.ss.squarehome2",
|
||||||
"com.treydev.pns"
|
"com.treydev.pns"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Be sure to keep these entries sorted alphabetically
|
// Be sure to keep these sections sorted alphabetically
|
||||||
public static Dictionary<string, KnownUsernameField> KnownUsernameFields => new List<KnownUsernameField>
|
public static Dictionary<string, KnownUsernameField> KnownUsernameFields => new List<KnownUsernameField>
|
||||||
{
|
{
|
||||||
new KnownUsernameField("accounts.google.com", "ServiceLogin", "Email"),
|
/**************************************************************************************
|
||||||
new KnownUsernameField("amazon.com", "signin", "ap_email_login"),
|
* SECTION A ——— World-renowned web sites/applications
|
||||||
new KnownUsernameField("github.com", "", "user[login]-footer"),
|
*************************************************************************************/
|
||||||
new KnownUsernameField("paypal.com", "signin", "email"),
|
|
||||||
new KnownUsernameField("signin.aws.amazon.com", "signin", "resolving_input"),
|
// REM.: For this type of web sites/applications, the Top 100 (SimilarWeb, 2019)
|
||||||
new KnownUsernameField("signin.ebay.com", "eBayISAPI.dll", "userid"),
|
// and the Top 50 (Alexa Internet, 2020) are covered. National variants
|
||||||
|
// have been added when available. Mobile and desktop versions supported.
|
||||||
|
//
|
||||||
|
// A few other popular web sites/applications have also been added.
|
||||||
|
//
|
||||||
|
// Could not be added, however:
|
||||||
|
// web sites/applications that don't use an "id" attribute for their login field.
|
||||||
|
|
||||||
|
// NOTE: The case of OAuth compatible web sites/applications that also provide
|
||||||
|
// a "user ID only" login page in this situation
|
||||||
|
// was taken into account in the tests as well.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Amazon ——— ap_email_login = mobile / ap_email = desktop (amazon.co.jp currently uses ap_email in both cases, as of July 2020).
|
||||||
|
new KnownUsernameField("amazon.ae", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.ca", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.cn", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.co.jp", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.co.uk", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.com", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.com.au", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.com.br", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.com.mx", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.com.tr", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.de", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.es", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.fr", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.in", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.it", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.nl", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.pl", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.sa", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.se", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
new KnownUsernameField("amazon.sg", new (string, string)[] { ("contains:/ap/signin", "ap_email_login,ap_email") }),
|
||||||
|
|
||||||
|
// Amazon Web Services
|
||||||
|
new KnownUsernameField("signin.aws.amazon.com", new (string, string)[] { ("signin", "resolving_input") }),
|
||||||
|
|
||||||
|
// Atlassian
|
||||||
|
new KnownUsernameField("id.atlassian.com", new (string, string)[] { ("login", "username") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* B
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Bitly ——— enterprise users.
|
||||||
|
new KnownUsernameField("bitly.com", new (string, string)[] { ("/sso/url_slug", "url_slug") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* E
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eBay ——— 1st = traditional access / 2nd = direct access (i.e. https://signin.ebay.tld/).
|
||||||
|
new KnownUsernameField("signin.befr.ebay.be", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.benl.ebay.be", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.cafr.ebay.ca", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.at", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.be", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.ca", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.ch", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.co.uk", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.com", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.com.au", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.com.hk", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.com.my", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.com.sg", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.de", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.es", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.fr", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.ie", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.in", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.it", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.nl", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.ph", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
new KnownUsernameField("signin.ebay.pl", new (string, string)[] { ("iendswith:eBayISAPI.dll", "userid"), ("icontains:/signin/", "userid") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Google ——— 1st = used in most cases (v2) / 2nd = used in some cases (v1).
|
||||||
|
new KnownUsernameField("accounts.google.com", new (string, string)[] { ("identifier", "identifierId"), ("ServiceLogin", "Email") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* P
|
||||||
|
*/
|
||||||
|
|
||||||
|
// PayPal ——— 1st = traditional access / 2nd = access using OAuth.
|
||||||
|
new KnownUsernameField("paypal.com", new (string, string)[] { ("signin", "email"), ("contains:/connect/", "email") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* T
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Tumblr ——— despite "signup" in its ID, it's the login field (the website offers registration if the account doesn't exist).
|
||||||
|
new KnownUsernameField("tumblr.com", new (string, string)[] { ("login", "signup_determine_email") }),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Y
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Yandex
|
||||||
|
new KnownUsernameField("passport.yandex.az", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.by", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.co.il", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.com", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.com.am", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.com.ge", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.com.tr", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.ee", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.fi", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.fr", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.kg", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.kz", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.lt", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.lv", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.md", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.pl", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.ru", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.tj", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.tm", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.ua", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
new KnownUsernameField("passport.yandex.uz", new (string, string)[] { ("auth", "passp-field-login") }),
|
||||||
|
|
||||||
|
/**************************************************************************************
|
||||||
|
* SECTION B ——— Top 100 worldwide
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
// As of July 2020, all entries that needed to be added from
|
||||||
|
// Top 100 (SimilarWeb, 2019) and Top 50 (Alexa Internet, 2020)
|
||||||
|
// matched section A.
|
||||||
|
//
|
||||||
|
// Therefore, no entry currently.
|
||||||
|
|
||||||
|
/**************************************************************************************
|
||||||
|
* SECTION C ——— Top 20 for selected countries
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
// REM.: For these selected countries, the Top 20 (SimilarWeb, 2020)
|
||||||
|
// and the Top 20 (Alexa Internet, 2020) are covered.
|
||||||
|
// Mobile and desktop versions supported.
|
||||||
|
//
|
||||||
|
// Could not be added, however:
|
||||||
|
// web sites/applications that don't use an "id" attribute for their login field.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Japan
|
||||||
|
*/
|
||||||
|
|
||||||
|
// NTT DOCOMO ——— mainly used for "My docomo".
|
||||||
|
new KnownUsernameField("cfg.smt.docomo.ne.jp", new (string, string)[] { ("contains:/auth/", "Di_Uid") }),
|
||||||
|
new KnownUsernameField("id.smt.docomo.ne.jp", new (string, string)[] { ("contains:/cgi7/", "Di_Uid") }),
|
||||||
|
|
||||||
|
/**************************************************************************************
|
||||||
|
* SECTION D ——— Miscellaneous
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Various entries ——— Following user requests, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// No entry, currently.
|
||||||
|
|
||||||
|
/**************************************************************************************
|
||||||
|
* SECTION Z ——— Special forms
|
||||||
|
*
|
||||||
|
* Despite "user ID + password" fields both visible, detection rules required.
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main
|
||||||
|
*/
|
||||||
|
|
||||||
|
// No entry, currently.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test/example purposes only
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GitHub ——— VERY special case (signup form, just to test the proper functioning of special forms).
|
||||||
|
new KnownUsernameField("github.com", new (string, string)[] { ("", "user[login]-footer") }),
|
||||||
}.ToDictionary(n => n.UriAuthority);
|
}.ToDictionary(n => n.UriAuthority);
|
||||||
|
|
||||||
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
|
public static void PrintTestData(AccessibilityNodeInfo root, AccessibilityEvent e)
|
||||||
@@ -198,12 +405,12 @@ namespace Bit.Droid.Accessibility
|
|||||||
var hasHttpProtocol = uri.StartsWith("http://") || uri.StartsWith("https://");
|
var hasHttpProtocol = uri.StartsWith("http://") || uri.StartsWith("https://");
|
||||||
if (!hasHttpProtocol && uri.Contains("."))
|
if (!hasHttpProtocol && uri.Contains("."))
|
||||||
{
|
{
|
||||||
if (Uri.TryCreate("http://" + uri, UriKind.Absolute, out var uri2))
|
if (Uri.TryCreate("https://" + uri, UriKind.Absolute, out var _))
|
||||||
{
|
{
|
||||||
return string.Concat("http://", uri);
|
return string.Concat("https://", uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Uri.TryCreate(uri, UriKind.Absolute, out var uri3))
|
if (Uri.TryCreate(uri, UriKind.Absolute, out var _))
|
||||||
{
|
{
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
@@ -307,11 +514,13 @@ namespace Bit.Droid.Accessibility
|
|||||||
public static AccessibilityNodeInfo GetUsernameEditText(string uriString,
|
public static AccessibilityNodeInfo GetUsernameEditText(string uriString,
|
||||||
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
IEnumerable<AccessibilityNodeInfo> allEditTexts)
|
||||||
{
|
{
|
||||||
|
string uriAuthority = null;
|
||||||
string uriKey = null;
|
string uriKey = null;
|
||||||
string uriLocalPath = null;
|
string uriLocalPath = null;
|
||||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
||||||
{
|
{
|
||||||
uriKey = uri.Authority;
|
uriAuthority = uri.Authority;
|
||||||
|
uriKey = uriAuthority.StartsWith("www.", StringComparison.Ordinal) ? uriAuthority.Substring(4) : uriAuthority;
|
||||||
uriLocalPath = uri.LocalPath;
|
uriLocalPath = uri.LocalPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,15 +536,64 @@ namespace Bit.Droid.Accessibility
|
|||||||
if (KnownUsernameFields.ContainsKey(uriKey))
|
if (KnownUsernameFields.ContainsKey(uriKey))
|
||||||
{
|
{
|
||||||
var usernameField = KnownUsernameFields[uriKey];
|
var usernameField = KnownUsernameFields[uriKey];
|
||||||
if (uriLocalPath.EndsWith(usernameField.UriPathEnd))
|
(string UriPathWanted, string UsernameViewId)[] accessOptions = usernameField.AccessOptions;
|
||||||
|
|
||||||
|
for (int i = 0; i < accessOptions.Length; i++)
|
||||||
{
|
{
|
||||||
foreach (var editText in allEditTexts)
|
string curUriPathWanted = accessOptions[i].UriPathWanted;
|
||||||
|
string curUsernameViewId = accessOptions[i].UsernameViewId;
|
||||||
|
bool uriLocalPathMatches = false;
|
||||||
|
|
||||||
|
// Case-sensitive comparison
|
||||||
|
if (curUriPathWanted.StartsWith("startswith:", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
foreach (var usernameViewId in usernameField.UsernameViewId.Split(","))
|
curUriPathWanted = curUriPathWanted.Substring(11);
|
||||||
|
uriLocalPathMatches = uriLocalPath.StartsWith(curUriPathWanted, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
else if (curUriPathWanted.StartsWith("contains:", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curUriPathWanted = curUriPathWanted.Substring(9);
|
||||||
|
uriLocalPathMatches = uriLocalPath.Contains(curUriPathWanted, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
else if (curUriPathWanted.StartsWith("endswith:", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curUriPathWanted = curUriPathWanted.Substring(9);
|
||||||
|
uriLocalPathMatches = uriLocalPath.EndsWith(curUriPathWanted, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case-insensitive comparison
|
||||||
|
else if (curUriPathWanted.StartsWith("istartswith:", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curUriPathWanted = curUriPathWanted.Substring(12);
|
||||||
|
uriLocalPathMatches = uriLocalPath.StartsWith(curUriPathWanted, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (curUriPathWanted.StartsWith("icontains:", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curUriPathWanted = curUriPathWanted.Substring(10);
|
||||||
|
uriLocalPathMatches = uriLocalPath.Contains(curUriPathWanted, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (curUriPathWanted.StartsWith("iendswith:", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curUriPathWanted = curUriPathWanted.Substring(10);
|
||||||
|
uriLocalPathMatches = uriLocalPath.EndsWith(curUriPathWanted, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default type of comparison
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uriLocalPathMatches = uriLocalPath.EndsWith(curUriPathWanted, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uriLocalPathMatches)
|
||||||
|
{
|
||||||
|
foreach (var editText in allEditTexts)
|
||||||
{
|
{
|
||||||
if (usernameViewId == editText.ViewIdResourceName)
|
foreach (var usernameViewId in curUsernameViewId.Split(","))
|
||||||
{
|
{
|
||||||
return editText;
|
if (usernameViewId == editText.ViewIdResourceName)
|
||||||
|
{
|
||||||
|
return editText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,7 +694,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
var icon = (ImageView)view.FindViewById(Resource.Id.icon);
|
var icon = (ImageView)view.FindViewById(Resource.Id.icon);
|
||||||
text1.Text = AppResources.AutofillWithBitwarden;
|
text1.Text = AppResources.AutofillWithBitwarden;
|
||||||
text2.Text = AppResources.GoToMyVault;
|
text2.Text = AppResources.GoToMyVault;
|
||||||
icon.SetImageResource(Resource.Drawable.icon);
|
icon.SetImageResource(Resource.Drawable.shield);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +859,12 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
public static bool IsAutofillServicePromptVisible(IEnumerable<AccessibilityWindowInfo> windows)
|
public static bool IsAutofillServicePromptVisible(IEnumerable<AccessibilityWindowInfo> windows)
|
||||||
{
|
{
|
||||||
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? false;
|
// Autofill framework not available until API 26
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||||
|
{
|
||||||
|
return windows?.Any(w => w.Title?.ToLower().Contains("autofill") ?? false) ?? false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetNodeHeight(AccessibilityNodeInfo node)
|
public static int GetNodeHeight(AccessibilityNodeInfo node)
|
||||||
|
|||||||
@@ -255,13 +255,13 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
if (!AccessibilityHelpers.OverlayPermitted())
|
if (!AccessibilityHelpers.OverlayPermitted())
|
||||||
{
|
{
|
||||||
if (!AccessibilityHelpers.IsAutofillTileAdded)
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
// The user has the option of only using the autofill tile and leaving the overlay permission
|
// The user has the option of only using the autofill tile and leaving the overlay permission
|
||||||
// disabled, so only show this toast if they're using accessibility without overlay permission and
|
// disabled, so only show this toast if they're using accessibility without overlay permission on
|
||||||
// have _not_ added the autofill tile
|
// a version of Android without quick-action tile support
|
||||||
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
|
System.Diagnostics.Debug.WriteLine(">>> Overlay Permission not granted");
|
||||||
Toast.MakeText(this, AppResources.AccessibilityOverlayPermissionAlert, ToastLength.Long).Show();
|
Toast.MakeText(this, AppResources.AccessibilityDrawOverPermissionAlert, ToastLength.Long).Show();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,13 @@
|
|||||||
{
|
{
|
||||||
public class KnownUsernameField
|
public class KnownUsernameField
|
||||||
{
|
{
|
||||||
public KnownUsernameField(string uriAuthority, string uriPathEnd, string usernameViewId)
|
public KnownUsernameField(string uriAuthority, (string UriPathWanted, string UsernameViewId)[] accessOptions)
|
||||||
{
|
{
|
||||||
UriAuthority = uriAuthority;
|
UriAuthority = uriAuthority;
|
||||||
UriPathEnd = uriPathEnd;
|
AccessOptions = accessOptions;
|
||||||
UsernameViewId = usernameViewId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string UriAuthority { get; set; }
|
public string UriAuthority { get; set; }
|
||||||
public string UriPathEnd { get; set; }
|
public (string UriPathWanted, string UsernameViewId)[] AccessOptions { get; set; }
|
||||||
public string UsernameViewId { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||||
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
|
||||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
<DefineConstants>DEBUG;</DefineConstants>
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>3</WarningLevel>
|
<WarningLevel>3</WarningLevel>
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
|
||||||
<AndroidSupportedAbis />
|
<AndroidSupportedAbis />
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -75,22 +74,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.6.7</Version>
|
<Version>1.8.10</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.9" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.11" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.10" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.5.3.1</Version>
|
<Version>1.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>71.1740.0</Version>
|
<Version>122.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.4.0.4" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" />
|
<PackageReference Include="Xamarin.Google.Dagger" Version="2.37.0" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" />
|
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||||
<Version>71.1600.0</Version>
|
<Version>117.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -110,22 +111,24 @@
|
|||||||
<Compile Include="Autofill\SavedItem.cs" />
|
<Compile Include="Autofill\SavedItem.cs" />
|
||||||
<Compile Include="Effects\FabShadowEffect.cs" />
|
<Compile Include="Effects\FabShadowEffect.cs" />
|
||||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
<Compile Include="Effects\FixedSizeEffect.cs" />
|
||||||
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
|
||||||
<Compile Include="Effects\TabBarEffect.cs" />
|
<Compile Include="Effects\TabBarEffect.cs" />
|
||||||
<Compile Include="Push\FirebaseInstanceIdService.cs" />
|
|
||||||
<Compile Include="Push\FirebaseMessagingService.cs" />
|
<Compile Include="Push\FirebaseMessagingService.cs" />
|
||||||
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
||||||
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
<Compile Include="Receivers\RestrictionsChangedReceiver.cs" />
|
||||||
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
<Compile Include="Receivers\EventUploadReceiver.cs" />
|
||||||
<Compile Include="Receivers\LockAlarmReceiver.cs" />
|
|
||||||
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
<Compile Include="Receivers\PackageReplacedReceiver.cs" />
|
||||||
<Compile Include="Renderers\CipherViewCellRenderer.cs" />
|
<Compile Include="Renderers\ExtendedGridRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\ExtendedStepperRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\CustomSwitchRenderer.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="Services\AndroidPushNotificationService.cs" />
|
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
||||||
<Compile Include="Services\AndroidLogService.cs" />
|
<Compile Include="Services\AndroidLogService.cs" />
|
||||||
@@ -133,6 +136,7 @@
|
|||||||
<Compile Include="MainActivity.cs" />
|
<Compile Include="MainActivity.cs" />
|
||||||
<Compile Include="Resources\Resource.designer.cs" />
|
<Compile Include="Resources\Resource.designer.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Services\BiometricService.cs" />
|
||||||
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
<Compile Include="Services\CryptoPrimitiveService.cs" />
|
||||||
<Compile Include="Services\DeviceActionService.cs" />
|
<Compile Include="Services\DeviceActionService.cs" />
|
||||||
<Compile Include="Services\LocalizeService.cs" />
|
<Compile Include="Services\LocalizeService.cs" />
|
||||||
@@ -141,13 +145,15 @@
|
|||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||||
<Compile Include="Utilities\AppCenterHelper.cs" />
|
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||||
|
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||||
|
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||||
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||||
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
|
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
|
||||||
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
|
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
|
||||||
<None Include="8bit.keystore.enc" />
|
<None Include="8bit.keystore.enc" />
|
||||||
<None Include="ci-build-apks.ps1" />
|
|
||||||
<GoogleServicesJson Include="google-services.json" />
|
<GoogleServicesJson Include="google-services.json" />
|
||||||
<GoogleServicesJson Include="google-services.json.enc" />
|
<GoogleServicesJson Include="google-services.json.enc" />
|
||||||
<None Include="fdroid-keystore.jks.enc" />
|
<None Include="fdroid-keystore.jks.enc" />
|
||||||
@@ -155,32 +161,32 @@
|
|||||||
<None Include="upload-keystore.jks.enc" />
|
<None Include="upload-keystore.jks.enc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_overlay.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\logo_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_permission.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\logo_white_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step1.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\logo_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\accessibility_step2.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\logo_white_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_enable.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\autofill_use.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_overlay.png" />
|
<AndroidResource Include="Resources\drawable\card.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_permission.png" />
|
<AndroidResource Include="Resources\drawable\cog.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step1.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\accessibility_step2.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_enable.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\autofill_use.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_overlay.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_permission.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step1.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\accessibility_step2.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_enable.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\autofill_use.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_overlay.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_permission.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_step1.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\accessibility_step2.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\autofill_enable.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\autofill_use.png" />
|
|
||||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||||
<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\info.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\lock.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\login.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\logo.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\logo_white.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\paper_plane.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\pencil.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\plus.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\refresh.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\search.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\shield.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
|
||||||
<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" />
|
||||||
@@ -195,6 +201,7 @@
|
|||||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||||
|
<AndroidResource Include="Resources\values-night\styles.xml" />
|
||||||
<AndroidResource Include="Resources\values\styles.xml" />
|
<AndroidResource Include="Resources\values\styles.xml" />
|
||||||
<AndroidResource Include="Resources\values\colors.xml" />
|
<AndroidResource Include="Resources\values\colors.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -203,7 +210,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\App\App.csproj">
|
<ProjectReference Include="..\App\App.csproj">
|
||||||
<Project>{9F1742A7-7D03-4BB3-8FCD-41BC3002B00A}</Project>
|
<Project>{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}</Project>
|
||||||
<Name>App</Name>
|
<Name>App</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Core\Core.csproj">
|
<ProjectReference Include="..\Core\Core.csproj">
|
||||||
@@ -232,138 +239,15 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
<AndroidResource Include="Resources\layout\autofill_listitem.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\login.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\login.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\login.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\login.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\login.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\logo.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\values-v21\styles.xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\refresh.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\lock.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\logo.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-hdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\yubikey.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\shield.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-xhdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\lock.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\refresh.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\refresh.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\lock.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\lock.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\refresh.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\lock.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\refresh.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\id.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\card.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\id.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\card.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\id.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\card.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\id.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\card.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\card.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\id.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\pencil.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\plus.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\pencil.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\plus.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\pencil.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\plus.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\pencil.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\plus.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\plus.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\pencil.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\slider_thumb.xml" />
|
<AndroidResource Include="Resources\drawable\slider_thumb.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -373,84 +257,12 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\logo_white.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\logo_white.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\search.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\search.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\search.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\search.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\search.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\cog.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\cog.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\cog.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\cog.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\cog.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\values\dimens.xml">
|
<AndroidResource Include="Resources\values\dimens.xml">
|
||||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\shield.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\shield.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\shield.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\shield.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable\refresh_sm.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-hdpi\refresh_sm.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xhdpi\refresh_sm.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\refresh_sm.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidResource Include="Resources\drawable-xxxhdpi\refresh_sm.png" />
|
|
||||||
</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>
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Service.Autofill;
|
using Android.Service.Autofill;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Android.App.Slices;
|
||||||
|
using Android.Graphics;
|
||||||
|
using Android.Graphics.Drawables;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using Android.Widget.Inline;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Android.Views.Autofill;
|
using Android.Views.Autofill;
|
||||||
|
using AndroidX.AutoFill.Inline;
|
||||||
|
using AndroidX.AutoFill.Inline.V1;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using SaveFlags = Android.Service.Autofill.SaveFlags;
|
||||||
|
|
||||||
namespace Bit.Droid.Autofill
|
namespace Bit.Droid.Autofill
|
||||||
{
|
{
|
||||||
@@ -25,6 +35,7 @@ namespace Bit.Droid.Autofill
|
|||||||
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
"com.duckduckgo.mobile.android",
|
"com.duckduckgo.mobile.android",
|
||||||
|
"com.google.android.googlequicksearchbox",
|
||||||
"org.mozilla.focus",
|
"org.mozilla.focus",
|
||||||
"org.mozilla.klar",
|
"org.mozilla.klar",
|
||||||
};
|
};
|
||||||
@@ -41,6 +52,7 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.amazon.cloud9",
|
"com.amazon.cloud9",
|
||||||
"com.android.browser",
|
"com.android.browser",
|
||||||
"com.android.chrome",
|
"com.android.chrome",
|
||||||
|
"com.android.htmlviewer",
|
||||||
"com.avast.android.secure.browser",
|
"com.avast.android.secure.browser",
|
||||||
"com.avg.android.secure.browser",
|
"com.avg.android.secure.browser",
|
||||||
"com.brave.browser",
|
"com.brave.browser",
|
||||||
@@ -51,11 +63,21 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.chrome.beta",
|
"com.chrome.beta",
|
||||||
"com.chrome.canary",
|
"com.chrome.canary",
|
||||||
"com.chrome.dev",
|
"com.chrome.dev",
|
||||||
|
"com.cookiegames.smartcookie",
|
||||||
|
"com.cookiejarapps.android.smartcookieweb",
|
||||||
"com.ecosia.android",
|
"com.ecosia.android",
|
||||||
"com.google.android.apps.chrome",
|
"com.google.android.apps.chrome",
|
||||||
"com.google.android.apps.chrome_dev",
|
"com.google.android.apps.chrome_dev",
|
||||||
|
"com.google.android.captiveportallogin",
|
||||||
|
"com.jamal2367.styx",
|
||||||
"com.kiwibrowser.browser",
|
"com.kiwibrowser.browser",
|
||||||
"com.microsoft.emmx",
|
"com.microsoft.emmx",
|
||||||
|
"com.microsoft.emmx.beta",
|
||||||
|
"com.microsoft.emmx.canary",
|
||||||
|
"com.microsoft.emmx.dev",
|
||||||
|
"com.mmbox.browser",
|
||||||
|
"com.mmbox.xbrowser",
|
||||||
|
"com.mycompany.app.soulbrowser",
|
||||||
"com.naver.whale",
|
"com.naver.whale",
|
||||||
"com.opera.browser",
|
"com.opera.browser",
|
||||||
"com.opera.browser.beta",
|
"com.opera.browser.beta",
|
||||||
@@ -71,7 +93,17 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.vivaldi.browser.snapshot",
|
"com.vivaldi.browser.snapshot",
|
||||||
"com.vivaldi.browser.sopranos",
|
"com.vivaldi.browser.sopranos",
|
||||||
"com.yandex.browser",
|
"com.yandex.browser",
|
||||||
|
"com.z28j.feel",
|
||||||
|
"idm.internet.download.manager",
|
||||||
|
"idm.internet.download.manager.adm.lite",
|
||||||
|
"idm.internet.download.manager.plus",
|
||||||
|
"io.github.forkmaintainers.iceraven",
|
||||||
|
"mark.via",
|
||||||
"mark.via.gp",
|
"mark.via.gp",
|
||||||
|
"net.slions.fulguris.full.download",
|
||||||
|
"net.slions.fulguris.full.download.debug",
|
||||||
|
"net.slions.fulguris.full.playstore",
|
||||||
|
"net.slions.fulguris.full.playstore.debug",
|
||||||
"org.adblockplus.browser",
|
"org.adblockplus.browser",
|
||||||
"org.adblockplus.browser.beta",
|
"org.adblockplus.browser.beta",
|
||||||
"org.bromite.bromite",
|
"org.bromite.bromite",
|
||||||
@@ -89,6 +121,9 @@ namespace Bit.Droid.Autofill
|
|||||||
"org.mozilla.rocket",
|
"org.mozilla.rocket",
|
||||||
"org.torproject.torbrowser",
|
"org.torproject.torbrowser",
|
||||||
"org.torproject.torbrowser_alpha",
|
"org.torproject.torbrowser_alpha",
|
||||||
|
"org.ungoogled.chromium.extensions.stable",
|
||||||
|
"org.ungoogled.chromium.stable",
|
||||||
|
"us.spotco.fennec_dos",
|
||||||
};
|
};
|
||||||
|
|
||||||
// The URLs are blacklisted from autofilling
|
// The URLs are blacklisted from autofilling
|
||||||
@@ -109,25 +144,61 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
var allCiphers = ciphers.Item1.ToList();
|
var allCiphers = ciphers.Item1.ToList();
|
||||||
allCiphers.AddRange(ciphers.Item2.ToList());
|
allCiphers.AddRange(ciphers.Item2.ToList());
|
||||||
return allCiphers.Select(c => new FilledItem(c)).ToList();
|
var nonPromptCiphers = allCiphers.Where(cipher => cipher.Reprompt == CipherRepromptType.None);
|
||||||
|
return nonPromptCiphers.Select(c => new FilledItem(c)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (parser.FieldCollection.FillableForCard)
|
else if (parser.FieldCollection.FillableForCard)
|
||||||
{
|
{
|
||||||
var ciphers = await cipherService.GetAllDecryptedAsync();
|
var ciphers = await cipherService.GetAllDecryptedAsync();
|
||||||
return ciphers.Where(c => c.Type == CipherType.Card).Select(c => new FilledItem(c)).ToList();
|
return ciphers.Where(c => c.Type == CipherType.Card && c.Reprompt == CipherRepromptType.None).Select(c => new FilledItem(c)).ToList();
|
||||||
}
|
}
|
||||||
return new List<FilledItem>();
|
return new List<FilledItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FillResponse BuildFillResponse(Parser parser, List<FilledItem> items, bool locked)
|
public static FillResponse BuildFillResponse(Parser parser, List<FilledItem> items, bool locked,
|
||||||
|
bool inlineAutofillEnabled, FillRequest fillRequest = null)
|
||||||
{
|
{
|
||||||
|
// Acquire inline presentation specs on Android 11+
|
||||||
|
IList<InlinePresentationSpec> inlinePresentationSpecs = null;
|
||||||
|
var inlinePresentationSpecsCount = 0;
|
||||||
|
var inlineMaxSuggestedCount = 0;
|
||||||
|
if (inlineAutofillEnabled && fillRequest != null && (int)Build.VERSION.SdkInt >= 30)
|
||||||
|
{
|
||||||
|
var inlineSuggestionsRequest = fillRequest.InlineSuggestionsRequest;
|
||||||
|
inlineMaxSuggestedCount = inlineSuggestionsRequest?.MaxSuggestionCount ?? 0;
|
||||||
|
inlinePresentationSpecs = inlineSuggestionsRequest?.InlinePresentationSpecs;
|
||||||
|
inlinePresentationSpecsCount = inlinePresentationSpecs?.Count ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build response
|
||||||
var responseBuilder = new FillResponse.Builder();
|
var responseBuilder = new FillResponse.Builder();
|
||||||
if (items != null && items.Count > 0)
|
if (items != null && items.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
var maxItems = items.Count;
|
||||||
|
if (inlineMaxSuggestedCount > 0)
|
||||||
{
|
{
|
||||||
var dataset = BuildDataset(parser.ApplicationContext, parser.FieldCollection, item);
|
// -1 to adjust for 'open vault' option
|
||||||
|
maxItems = Math.Min(maxItems, inlineMaxSuggestedCount - 1);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < maxItems; i++)
|
||||||
|
{
|
||||||
|
InlinePresentationSpec inlinePresentationSpec = null;
|
||||||
|
if (inlinePresentationSpecs != null)
|
||||||
|
{
|
||||||
|
if (i < inlinePresentationSpecsCount)
|
||||||
|
{
|
||||||
|
inlinePresentationSpec = inlinePresentationSpecs[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the max suggestion count is larger than the number of specs in the list, then
|
||||||
|
// the last spec is used for the remainder of the suggestions
|
||||||
|
inlinePresentationSpec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dataset = BuildDataset(parser.ApplicationContext, parser.FieldCollection, items[i],
|
||||||
|
inlinePresentationSpec);
|
||||||
if (dataset != null)
|
if (dataset != null)
|
||||||
{
|
{
|
||||||
responseBuilder.AddDataset(dataset);
|
responseBuilder.AddDataset(dataset);
|
||||||
@@ -135,16 +206,34 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseBuilder.AddDataset(BuildVaultDataset(parser.ApplicationContext, parser.FieldCollection,
|
responseBuilder.AddDataset(BuildVaultDataset(parser.ApplicationContext, parser.FieldCollection,
|
||||||
parser.Uri, locked));
|
parser.Uri, locked, inlinePresentationSpecs));
|
||||||
AddSaveInfo(parser, responseBuilder, parser.FieldCollection);
|
AddSaveInfo(parser, fillRequest, responseBuilder, parser.FieldCollection);
|
||||||
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
|
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
|
||||||
return responseBuilder.Build();
|
return responseBuilder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem)
|
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem,
|
||||||
|
InlinePresentationSpec inlinePresentationSpec = null)
|
||||||
{
|
{
|
||||||
var datasetBuilder = new Dataset.Builder(
|
var overlayPresentation = BuildOverlayPresentation(
|
||||||
BuildListView(filledItem.Name, filledItem.Subtitle, filledItem.Icon, context));
|
filledItem.Name,
|
||||||
|
filledItem.Subtitle,
|
||||||
|
filledItem.Icon,
|
||||||
|
context);
|
||||||
|
|
||||||
|
var inlinePresentation = BuildInlinePresentation(
|
||||||
|
inlinePresentationSpec,
|
||||||
|
filledItem.Name,
|
||||||
|
filledItem.Subtitle,
|
||||||
|
filledItem.Icon,
|
||||||
|
null,
|
||||||
|
context);
|
||||||
|
|
||||||
|
var datasetBuilder = new Dataset.Builder(overlayPresentation);
|
||||||
|
if (inlinePresentation != null)
|
||||||
|
{
|
||||||
|
datasetBuilder.SetInlinePresentation(inlinePresentation);
|
||||||
|
}
|
||||||
if (filledItem.ApplyToFields(fields, datasetBuilder))
|
if (filledItem.ApplyToFields(fields, datasetBuilder))
|
||||||
{
|
{
|
||||||
return datasetBuilder.Build();
|
return datasetBuilder.Build();
|
||||||
@@ -152,7 +241,8 @@ namespace Bit.Droid.Autofill
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dataset BuildVaultDataset(Context context, FieldCollection fields, string uri, bool locked)
|
public static Dataset BuildVaultDataset(Context context, FieldCollection fields, string uri, bool locked,
|
||||||
|
IList<InlinePresentationSpec> inlinePresentationSpecs = null)
|
||||||
{
|
{
|
||||||
var intent = new Intent(context, typeof(MainActivity));
|
var intent = new Intent(context, typeof(MainActivity));
|
||||||
intent.PutExtra("autofillFramework", true);
|
intent.PutExtra("autofillFramework", true);
|
||||||
@@ -176,14 +266,26 @@ namespace Bit.Droid.Autofill
|
|||||||
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
var pendingIntent = PendingIntent.GetActivity(context, ++_pendingIntentId, intent,
|
||||||
PendingIntentFlags.CancelCurrent);
|
PendingIntentFlags.CancelCurrent);
|
||||||
|
|
||||||
var view = BuildListView(
|
var overlayPresentation = BuildOverlayPresentation(
|
||||||
AppResources.AutofillWithBitwarden,
|
AppResources.AutofillWithBitwarden,
|
||||||
locked ? AppResources.VaultIsLocked : AppResources.GoToMyVault,
|
locked ? AppResources.VaultIsLocked : AppResources.GoToMyVault,
|
||||||
Resource.Drawable.icon,
|
Resource.Drawable.icon,
|
||||||
context);
|
context);
|
||||||
|
|
||||||
var datasetBuilder = new Dataset.Builder(view);
|
var inlinePresentation = BuildInlinePresentation(
|
||||||
datasetBuilder.SetAuthentication(pendingIntent.IntentSender);
|
inlinePresentationSpecs?.Last(),
|
||||||
|
AppResources.Bitwarden,
|
||||||
|
locked ? AppResources.VaultIsLocked : AppResources.MyVault,
|
||||||
|
Resource.Drawable.icon,
|
||||||
|
pendingIntent,
|
||||||
|
context);
|
||||||
|
|
||||||
|
var datasetBuilder = new Dataset.Builder(overlayPresentation);
|
||||||
|
if (inlinePresentation != null)
|
||||||
|
{
|
||||||
|
datasetBuilder.SetInlinePresentation(inlinePresentation);
|
||||||
|
}
|
||||||
|
datasetBuilder.SetAuthentication(pendingIntent?.IntentSender);
|
||||||
|
|
||||||
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
|
// Dataset must have a value set. We will reset this in the main activity when the real item is chosen.
|
||||||
foreach (var autofillId in fields.AutofillIds)
|
foreach (var autofillId in fields.AutofillIds)
|
||||||
@@ -193,7 +295,7 @@ namespace Bit.Droid.Autofill
|
|||||||
return datasetBuilder.Build();
|
return datasetBuilder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RemoteViews BuildListView(string text, string subtext, int iconId, Context context)
|
public static RemoteViews BuildOverlayPresentation(string text, string subtext, int iconId, Context context)
|
||||||
{
|
{
|
||||||
var packageName = context.PackageName;
|
var packageName = context.PackageName;
|
||||||
var view = new RemoteViews(packageName, Resource.Layout.autofill_listitem);
|
var view = new RemoteViews(packageName, Resource.Layout.autofill_listitem);
|
||||||
@@ -203,11 +305,87 @@ namespace Bit.Droid.Autofill
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddSaveInfo(Parser parser, FillResponse.Builder responseBuilder, FieldCollection fields)
|
public static InlinePresentation BuildInlinePresentation(InlinePresentationSpec inlinePresentationSpec,
|
||||||
|
string text, string subtext, int iconId, PendingIntent pendingIntent, Context context)
|
||||||
|
{
|
||||||
|
if ((int)Build.VERSION.SdkInt < 30 || inlinePresentationSpec == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pendingIntent == null)
|
||||||
|
{
|
||||||
|
// InlinePresentation requires nonNull pending intent (even though we only utilize one for the
|
||||||
|
// "my vault" presentation) so we're including an empty one here
|
||||||
|
pendingIntent = PendingIntent.GetService(context, 0, new Intent(),
|
||||||
|
PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent);
|
||||||
|
}
|
||||||
|
var slice = CreateInlinePresentationSlice(
|
||||||
|
inlinePresentationSpec,
|
||||||
|
text,
|
||||||
|
subtext,
|
||||||
|
iconId,
|
||||||
|
"Autofill option",
|
||||||
|
pendingIntent,
|
||||||
|
context);
|
||||||
|
if (slice != null)
|
||||||
|
{
|
||||||
|
return new InlinePresentation(slice, inlinePresentationSpec, false);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Slice CreateInlinePresentationSlice(
|
||||||
|
InlinePresentationSpec inlinePresentationSpec,
|
||||||
|
string text,
|
||||||
|
string subtext,
|
||||||
|
int iconId,
|
||||||
|
string contentDescription,
|
||||||
|
PendingIntent pendingIntent,
|
||||||
|
Context context)
|
||||||
|
{
|
||||||
|
var imeStyle = inlinePresentationSpec.Style;
|
||||||
|
if (!UiVersions.GetVersions(imeStyle).Contains(UiVersions.InlineUiVersion1))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var contentBuilder = InlineSuggestionUi.NewContentBuilder(pendingIntent)
|
||||||
|
.SetContentDescription(contentDescription);
|
||||||
|
if (!string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
contentBuilder.SetTitle(text);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(subtext))
|
||||||
|
{
|
||||||
|
contentBuilder.SetSubtitle(subtext);
|
||||||
|
}
|
||||||
|
if (iconId > 0)
|
||||||
|
{
|
||||||
|
var icon = Icon.CreateWithResource(context, iconId);
|
||||||
|
if (icon != null)
|
||||||
|
{
|
||||||
|
if (iconId == Resource.Drawable.icon)
|
||||||
|
{
|
||||||
|
// Don't tint our logo
|
||||||
|
icon.SetTintBlendMode(BlendMode.Dst);
|
||||||
|
}
|
||||||
|
contentBuilder.SetStartIcon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contentBuilder.Build().JavaCast<InlineSuggestionUi.Content>()?.Slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddSaveInfo(Parser parser, FillRequest fillRequest, FillResponse.Builder responseBuilder,
|
||||||
|
FieldCollection fields)
|
||||||
{
|
{
|
||||||
// Docs state that password fields cannot be reliably saved in Compat mode since they will show as
|
// Docs state that password fields cannot be reliably saved in Compat mode since they will show as
|
||||||
// masked values.
|
// masked values.
|
||||||
var compatBrowser = CompatBrowsers.Contains(parser.PackageName);
|
bool? compatRequest = null;
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q && fillRequest != null)
|
||||||
|
{
|
||||||
|
// Attempt to automatically establish compat request mode on Android 10+
|
||||||
|
compatRequest = (fillRequest.Flags | FillRequest.FlagCompatibilityModeRequest) == fillRequest.Flags;
|
||||||
|
}
|
||||||
|
var compatBrowser = compatRequest ?? CompatBrowsers.Contains(parser.PackageName);
|
||||||
if (compatBrowser && fields.SaveType == SaveDataType.Password)
|
if (compatBrowser && fields.SaveType == SaveDataType.Password)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace Bit.Droid.Autofill
|
|||||||
private ICipherService _cipherService;
|
private ICipherService _cipherService;
|
||||||
private IVaultTimeoutService _vaultTimeoutService;
|
private IVaultTimeoutService _vaultTimeoutService;
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
|
private IPolicyService _policyService;
|
||||||
|
private IUserService _userService;
|
||||||
|
|
||||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||||
FillCallback callback)
|
FillCallback callback)
|
||||||
@@ -46,6 +48,8 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
||||||
|
|
||||||
if (_vaultTimeoutService == null)
|
if (_vaultTimeoutService == null)
|
||||||
{
|
{
|
||||||
@@ -53,6 +57,7 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<FilledItem> items = null;
|
List<FilledItem> items = null;
|
||||||
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
var locked = await _vaultTimeoutService.IsLockedAsync();
|
var locked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!locked)
|
if (!locked)
|
||||||
{
|
{
|
||||||
@@ -64,7 +69,7 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build response
|
// build response
|
||||||
var response = AutofillHelpers.BuildFillResponse(parser, items, locked);
|
var response = AutofillHelpers.BuildFillResponse(parser, items, locked, inlineAutofillEnabled, request);
|
||||||
callback.OnSuccess(response);
|
callback.OnSuccess(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +92,14 @@ namespace Bit.Droid.Autofill
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
|
||||||
|
var personalOwnershipPolicyApplies = await _policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
|
||||||
|
if (personalOwnershipPolicyApplies)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var parser = new Parser(structure, ApplicationContext);
|
var parser = new Parser(structure, ApplicationContext);
|
||||||
parser.Parse();
|
parser.Parse();
|
||||||
|
|
||||||
|
|||||||
@@ -221,4 +221,4 @@ namespace Bit.Droid.Autofill
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Effects;
|
using Bit.Droid.Effects;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ namespace Bit.Droid.Effects
|
|||||||
if (Control is Android.Widget.Button button)
|
if (Control is Android.Widget.Button button)
|
||||||
{
|
{
|
||||||
var gd = new GradientDrawable();
|
var gd = new GradientDrawable();
|
||||||
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
|
gd.SetColor(ThemeHelpers.FabColor);
|
||||||
gd.SetCornerRadius(100);
|
gd.SetCornerRadius(100);
|
||||||
|
|
||||||
button.SetBackground(gd);
|
button.SetBackground(gd);
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
using Android.Widget;
|
|
||||||
using Bit.Droid.Effects;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportEffect(typeof(SelectableLabelEffect), "SelectableLabelEffect")]
|
|
||||||
namespace Bit.Droid.Effects
|
|
||||||
{
|
|
||||||
public class SelectableLabelEffect : PlatformEffect
|
|
||||||
{
|
|
||||||
protected override void OnAttached()
|
|
||||||
{
|
|
||||||
if (Control is TextView textView)
|
|
||||||
{
|
|
||||||
textView.SetTextIsSelectable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetached()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,18 +15,32 @@ using Bit.Droid.Receivers;
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AndroidX.Core.Content;
|
using AndroidX.Core.Content;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using ZXing.Net.Mobile.Android;
|
||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
[Activity(
|
[Activity(
|
||||||
Label = "Bitwarden",
|
Label = "Bitwarden",
|
||||||
Icon = "@mipmap/ic_launcher",
|
Icon = "@mipmap/ic_launcher",
|
||||||
Theme = "@style/LightTheme.Splash",
|
Theme = "@style/LaunchTheme",
|
||||||
MainLauncher = true,
|
MainLauncher = true,
|
||||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
|
LaunchMode = LaunchMode.SingleTask,
|
||||||
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
||||||
|
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
||||||
|
ConfigChanges.Navigation | ConfigChanges.UiMode)]
|
||||||
|
[IntentFilter(
|
||||||
|
new[] { Intent.ActionSend },
|
||||||
|
Categories = new[] { Intent.CategoryDefault },
|
||||||
|
DataMimeTypes = new[]
|
||||||
|
{
|
||||||
|
@"application/*",
|
||||||
|
@"image/*",
|
||||||
|
@"video/*",
|
||||||
|
@"text/*"
|
||||||
|
})]
|
||||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
@@ -37,7 +51,6 @@ namespace Bit.Droid
|
|||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IStorageService _storageService;
|
private IStorageService _storageService;
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _vaultTimeoutAlarmPendingIntent;
|
|
||||||
private PendingIntent _clearClipboardPendingIntent;
|
private PendingIntent _clearClipboardPendingIntent;
|
||||||
private PendingIntent _eventUploadPendingIntent;
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
@@ -50,9 +63,6 @@ namespace Bit.Droid
|
|||||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
var alarmIntent = new Intent(this, typeof(LockAlarmReceiver));
|
|
||||||
_vaultTimeoutAlarmPendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent,
|
|
||||||
PendingIntentFlags.UpdateCurrent);
|
|
||||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
||||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
@@ -71,7 +81,6 @@ namespace Bit.Droid
|
|||||||
TabLayoutResource = Resource.Layout.Tabbar;
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
ToolbarResource = Resource.Layout.Toolbar;
|
ToolbarResource = Resource.Layout.Toolbar;
|
||||||
|
|
||||||
UpdateTheme(ThemeManager.GetTheme(true));
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
if (!CoreHelpers.InDebugMode())
|
||||||
{
|
{
|
||||||
@@ -90,20 +99,7 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "scheduleVaultTimeoutTimer")
|
if (message.Command == "startEventTimer")
|
||||||
{
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
var vaultTimeoutMinutes = (int)message.Data;
|
|
||||||
var vaultTimeoutMs = vaultTimeoutMinutes * 60000;
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + vaultTimeoutMs + 10;
|
|
||||||
alarmManager.Set(AlarmType.RtcWakeup, triggerMs, _vaultTimeoutAlarmPendingIntent);
|
|
||||||
}
|
|
||||||
else if (message.Command == "cancelVaultTimeoutTimer")
|
|
||||||
{
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
alarmManager.Cancel(_vaultTimeoutAlarmPendingIntent);
|
|
||||||
}
|
|
||||||
else if (message.Command == "startEventTimer")
|
|
||||||
{
|
{
|
||||||
StartEventAlarm();
|
StartEventAlarm();
|
||||||
}
|
}
|
||||||
@@ -121,7 +117,7 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
else if (message.Command == "updatedTheme")
|
else if (message.Command == "updatedTheme")
|
||||||
{
|
{
|
||||||
RestartApp();
|
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => AppearanceAdjustments());
|
||||||
}
|
}
|
||||||
else if (message.Command == "exit")
|
else if (message.Command == "exit")
|
||||||
{
|
{
|
||||||
@@ -143,6 +139,8 @@ namespace Bit.Droid
|
|||||||
protected override void OnResume()
|
protected override void OnResume()
|
||||||
{
|
{
|
||||||
base.OnResume();
|
base.OnResume();
|
||||||
|
Xamarin.Essentials.Platform.OnResume();
|
||||||
|
AppearanceAdjustments();
|
||||||
if (_deviceActionService.SupportsNfc())
|
if (_deviceActionService.SupportsNfc())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -151,31 +149,48 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
|
AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
{
|
{
|
||||||
base.OnNewIntent(intent);
|
base.OnNewIntent(intent);
|
||||||
if (intent.GetBooleanExtra("generatorTile", false))
|
try
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
if (intent.GetBooleanExtra("generatorTile", false))
|
||||||
if (_appOptions != null)
|
|
||||||
{
|
{
|
||||||
_appOptions.GeneratorTile = true;
|
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.GeneratorTile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intent.GetBooleanExtra("myVaultTile", false))
|
||||||
|
{
|
||||||
|
_messagingService.Send("popAllAndGoToTabMyVault");
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.MyVaultTile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||||
|
{
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.CreateSend = GetCreateSendRequest(intent);
|
||||||
|
}
|
||||||
|
_messagingService.Send("popAllAndGoToTabSend");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParseYubiKey(intent.DataString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (intent.GetBooleanExtra("myVaultTile", false))
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabMyVault");
|
System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", e.GetType(), e.StackTrace);
|
||||||
if (_appOptions != null)
|
|
||||||
{
|
|
||||||
_appOptions.MyVaultTile = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParseYubiKey(intent.DataString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,8 +208,7 @@ namespace Bit.Droid
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
ZXing.Net.Mobile.Forms.Android.PermissionsHandler.OnRequestPermissionsResult(
|
PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
requestCode, permissions, grantResults);
|
|
||||||
}
|
}
|
||||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
@@ -295,7 +309,8 @@ namespace Bit.Droid
|
|||||||
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
||||||
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
||||||
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
||||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false),
|
||||||
|
CreateSend = GetCreateSendRequest(Intent)
|
||||||
};
|
};
|
||||||
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||||
if (fillType > 0)
|
if (fillType > 0)
|
||||||
@@ -317,6 +332,42 @@ namespace Bit.Droid
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Tuple<SendType, string, byte[], string> GetCreateSendRequest(Intent intent)
|
||||||
|
{
|
||||||
|
if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||||
|
{
|
||||||
|
if ((intent.Flags & ActivityFlags.LaunchedFromHistory) == ActivityFlags.LaunchedFromHistory)
|
||||||
|
{
|
||||||
|
// don't re-deliver intent if resuming from app switcher
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var type = intent.Type;
|
||||||
|
if (type.Contains("text/"))
|
||||||
|
{
|
||||||
|
var subject = intent.GetStringExtra(Intent.ExtraSubject);
|
||||||
|
var text = intent.GetStringExtra(Intent.ExtraText);
|
||||||
|
return new Tuple<SendType, string, byte[], string>(SendType.Text, subject, null, text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var data = intent.ClipData?.GetItemAt(0);
|
||||||
|
var uri = data?.Uri;
|
||||||
|
var filename = AndroidHelpers.GetFileName(ApplicationContext, uri);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
stream.CopyTo(memoryStream);
|
||||||
|
return new Tuple<SendType, string, byte[], string>(SendType.File, filename, memoryStream.ToArray(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Java.IO.FileNotFoundException) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void ParseYubiKey(string data)
|
private void ParseYubiKey(string data)
|
||||||
{
|
{
|
||||||
if (data == null)
|
if (data == null)
|
||||||
@@ -331,34 +382,11 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTheme(string theme)
|
private void AppearanceAdjustments()
|
||||||
{
|
{
|
||||||
if (theme == "dark")
|
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||||
{
|
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||||
SetTheme(Resource.Style.DarkTheme);
|
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
||||||
}
|
|
||||||
else if (theme == "black")
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.BlackTheme);
|
|
||||||
}
|
|
||||||
else if (theme == "nord")
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.NordTheme);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.LightTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestartApp()
|
|
||||||
{
|
|
||||||
var intent = new Intent(this, typeof(MainActivity));
|
|
||||||
var pendingIntent = PendingIntent.GetActivity(this, 5923650, intent, PendingIntentFlags.CancelCurrent);
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + 500;
|
|
||||||
alarmManager.Set(AlarmType.Rtc, triggerMs, pendingIntent);
|
|
||||||
Java.Lang.JavaSystem.Exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitApp()
|
private void ExitApp()
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
RegisterLocalServices();
|
RegisterLocalServices();
|
||||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
ServiceContainer.Init(deviceActionService.DeviceUserAgent);
|
ServiceContainer.Init(deviceActionService.DeviceUserAgent, Constants.ClearCiphersCacheKey,
|
||||||
|
Constants.AndroidAllClearCipherCacheKeys);
|
||||||
}
|
}
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||||
@@ -86,7 +87,6 @@ namespace Bit.Droid
|
|||||||
var preferencesStorage = new PreferencesStorageService(null);
|
var preferencesStorage = new PreferencesStorageService(null);
|
||||||
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
|
||||||
var liteDbStorage = new LiteDbStorageService(Path.Combine(documentsPath, "bitwarden.db"));
|
var liteDbStorage = new LiteDbStorageService(Path.Combine(documentsPath, "bitwarden.db"));
|
||||||
liteDbStorage.InitAsync();
|
|
||||||
var localizeService = new LocalizeService();
|
var localizeService = new LocalizeService();
|
||||||
var broadcasterService = new BroadcasterService();
|
var broadcasterService = new BroadcasterService();
|
||||||
var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
|
var messagingService = new MobileBroadcasterMessagingService(broadcasterService);
|
||||||
@@ -98,6 +98,10 @@ namespace Bit.Droid
|
|||||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
|
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);
|
||||||
@@ -108,6 +112,10 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
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<ICryptoFunctionService>("cryptoFunctionService", cryptoFunctionService);
|
||||||
|
ServiceContainer.Register<ICryptoService>("cryptoService", cryptoService);
|
||||||
|
ServiceContainer.Register<IPasswordRepromptService>("passwordRepromptService", passwordRepromptService);
|
||||||
|
|
||||||
// Push
|
// Push
|
||||||
#if FDROID
|
#if FDROID
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
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.4.3"
|
android:versionName="2.14.3"
|
||||||
|
android:installLocation="internalOnly"
|
||||||
package="com.x8bit.bitwarden">
|
package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Bitwarden"
|
android:label="Bitwarden"
|
||||||
android:theme="@style/LightTheme.Splash"
|
android:theme="@style/LaunchTheme"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
tools:replace="android:allowBackup"
|
tools:replace="android:allowBackup"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@@ -39,20 +40,6 @@
|
|||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
|
|
||||||
android:exported="false" />
|
|
||||||
<receiver
|
|
||||||
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="com.google.android.c2dm.permission.SEND">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
|
||||||
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
|
|
||||||
<category android:name="com.x8bit.bitwarden" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||||
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
<meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions" />
|
||||||
|
|
||||||
@@ -63,4 +50,12 @@
|
|||||||
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
||||||
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
|
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<!-- Package visibility (for Android 11+) -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="*"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#if !FDROID
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Firebase.Iid;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Push
|
|
||||||
{
|
|
||||||
[Service]
|
|
||||||
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
|
|
||||||
public class FirebaseInstanceIdService : Firebase.Iid.FirebaseInstanceIdService
|
|
||||||
{
|
|
||||||
public async override void OnTokenRefresh()
|
|
||||||
{
|
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
|
||||||
await storageService.SaveAsync(Constants.PushRegisteredTokenKey, FirebaseInstanceId.Instance.Token);
|
|
||||||
await pushNotificationService.RegisterAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Firebase.Messaging;
|
using Firebase.Messaging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -10,10 +10,19 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.Droid.Push
|
namespace Bit.Droid.Push
|
||||||
{
|
{
|
||||||
[Service]
|
[Service(Exported=false)]
|
||||||
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
|
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
|
||||||
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
|
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
|
||||||
{
|
{
|
||||||
|
public async override void OnNewToken(string token)
|
||||||
|
{
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
|
|
||||||
|
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
||||||
|
await pushNotificationService.RegisterAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async override void OnMessageReceived(RemoteMessage message)
|
public async override void OnMessageReceived(RemoteMessage message)
|
||||||
{
|
{
|
||||||
if (message?.Data == null)
|
if (message?.Data == null)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Bit.Droid.Receivers
|
|||||||
public override void OnReceive(Context context, Intent intent)
|
public override void OnReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
var clipboardManager = context.GetSystemService(Context.ClipboardService) as ClipboardManager;
|
||||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", string.Empty);
|
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using Android.Content;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Receivers
|
|
||||||
{
|
|
||||||
[BroadcastReceiver(Name = "com.x8bit.bitwarden.LockAlarmReceiver", Exported = false)]
|
|
||||||
public class LockAlarmReceiver : BroadcastReceiver
|
|
||||||
{
|
|
||||||
public async override void OnReceive(Context context, Intent intent)
|
|
||||||
{
|
|
||||||
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
|
||||||
await vaultTimeoutService.CheckVaultTimeoutAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.Graphics;
|
|
||||||
using Android.Runtime;
|
|
||||||
using Android.Util;
|
|
||||||
using Android.Views;
|
|
||||||
using Android.Views.InputMethods;
|
|
||||||
using Android.Widget;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using FFImageLoading;
|
|
||||||
using FFImageLoading.Views;
|
|
||||||
using FFImageLoading.Work;
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(CipherViewCell), typeof(CipherViewCellRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class CipherViewCellRenderer : ViewCellRenderer
|
|
||||||
{
|
|
||||||
private static Typeface _faTypeface;
|
|
||||||
private static Typeface _miTypeface;
|
|
||||||
private static Android.Graphics.Color _textColor;
|
|
||||||
private static Android.Graphics.Color _mutedColor;
|
|
||||||
private static Android.Graphics.Color _disabledIconColor;
|
|
||||||
|
|
||||||
private AndroidCipherCell _cell;
|
|
||||||
|
|
||||||
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
|
|
||||||
ViewGroup parent, Context context)
|
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_mutedColor == default(Android.Graphics.Color))
|
|
||||||
{
|
|
||||||
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
|
||||||
}
|
|
||||||
if (_disabledIconColor == default(Android.Graphics.Color))
|
|
||||||
{
|
|
||||||
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
|
|
||||||
}
|
|
||||||
|
|
||||||
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,6 +1,9 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
@@ -33,5 +37,33 @@ namespace Bit.Droid.Renderers
|
|||||||
(ImeAction)ImeFlags.NoExtractUi;
|
(ImeAction)ImeFlags.NoExtractUi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Entry.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
|
using Android.Graphics;
|
||||||
|
using Android.Text;
|
||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
|
using Android.Widget;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -16,6 +22,7 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
@@ -24,5 +31,68 @@ namespace Bit.Droid.Renderers
|
|||||||
(ImeAction)ImeFlags.NoExtractUi;
|
(ImeAction)ImeFlags.NoExtractUi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for failure to disable text prediction on non-password fields
|
||||||
|
// see https://github.com/xamarin/Xamarin.Forms/issues/10857
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
// Check if changed property is "IsPassword", otherwise ignore
|
||||||
|
if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
|
||||||
|
{
|
||||||
|
// Check if field type is text, otherwise ignore (numeric passwords, etc.)
|
||||||
|
EditText.InputType = Element.Keyboard.ToInputType();
|
||||||
|
bool isText = (EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText,
|
||||||
|
isNumber = (EditText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber;
|
||||||
|
if (isText || isNumber)
|
||||||
|
{
|
||||||
|
if (Element.IsPassword)
|
||||||
|
{
|
||||||
|
// Element is a password field, set inputType to TextVariationPassword which disables
|
||||||
|
// predictive text by default
|
||||||
|
EditText.InputType = EditText.InputType |
|
||||||
|
(isText ? InputTypes.TextVariationPassword : InputTypes.NumberVariationPassword);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Element is not a password field, set inputType to TextVariationVisiblePassword to
|
||||||
|
// disable predictive text while still displaying the content.
|
||||||
|
EditText.InputType = EditText.InputType |
|
||||||
|
(isText ? InputTypes.TextVariationVisiblePassword : InputTypes.NumberVariationNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The workaround above forces a reset of the style properties, so we need to re-apply the font.
|
||||||
|
// see https://xamarin.github.io/bugzilla-archives/33/33666/bug.html
|
||||||
|
var typeface = Typeface.CreateFromAsset(Context.Assets, "RobotoMono_Regular.ttf");
|
||||||
|
if (Control is TextView label)
|
||||||
|
{
|
||||||
|
label.Typeface = typeface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -15,11 +18,40 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
Control.PaddingBottom + 20);
|
Control.PaddingBottom + 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Picker.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/Android/Renderers/CustomSwitchRenderer.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
|
using Android.Graphics.Drawables;
|
||||||
|
using Android.OS;
|
||||||
|
using AndroidX.Core.Content.Resources;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Switch), typeof(CustomSwitchRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class CustomSwitchRenderer : SwitchRenderer
|
||||||
|
{
|
||||||
|
public CustomSwitchRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
UpdateColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Switch.OnColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateColors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColors()
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
|
||||||
|
{
|
||||||
|
// Android 5.x doesn't support ThumbTintList, and using SwitchCompat on every version after 5.x
|
||||||
|
// doesn't apply tinting the way we want. Let 5.x to do its own thing here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null);
|
||||||
|
if (t is GradientDrawable thumb)
|
||||||
|
{
|
||||||
|
Control.ThumbDrawable = thumb;
|
||||||
|
}
|
||||||
|
var thumbStates = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateChecked }, // checked
|
||||||
|
new[] { -Android.Resource.Attribute.StateChecked }, // unchecked
|
||||||
|
};
|
||||||
|
var thumbColors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.SwitchOnColor,
|
||||||
|
ThemeHelpers.SwitchThumbColor
|
||||||
|
};
|
||||||
|
Control.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/Android/Renderers/CustomTabbedRenderer.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Android.Views;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Google.Android.Material.BottomNavigation;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
using Xamarin.Forms.Platform.Android.AppCompat;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class CustomTabbedRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemReselectedListener
|
||||||
|
{
|
||||||
|
private TabbedPage _page;
|
||||||
|
|
||||||
|
public CustomTabbedRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
if (e.NewElement != null)
|
||||||
|
{
|
||||||
|
_page = e.NewElement;
|
||||||
|
GetBottomNavigationView()?.SetOnNavigationItemReselectedListener(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_page = e.OldElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BottomNavigationView GetBottomNavigationView()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < ViewGroup.ChildCount; i++)
|
||||||
|
{
|
||||||
|
var childView = ViewGroup.GetChildAt(i);
|
||||||
|
if (childView is ViewGroup viewGroup)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < viewGroup.ChildCount; j++)
|
||||||
|
{
|
||||||
|
var childRelativeLayoutView = viewGroup.GetChildAt(j);
|
||||||
|
if (childRelativeLayoutView is BottomNavigationView bottomNavigationView)
|
||||||
|
{
|
||||||
|
return bottomNavigationView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNavigationItemReselected(IMenuItem item)
|
||||||
|
{
|
||||||
|
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Android/Renderers/ExtendedDatePickerRenderer.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
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(ExtendedDatePicker), typeof(ExtendedDatePickerRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedDatePickerRenderer : DatePickerRenderer
|
||||||
|
{
|
||||||
|
public ExtendedDatePickerRenderer(Context context)
|
||||||
|
: base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
if (Control != null && Element is ExtendedDatePicker element)
|
||||||
|
{
|
||||||
|
// center text
|
||||||
|
Control.Gravity = GravityFlags.CenterHorizontal;
|
||||||
|
|
||||||
|
// use placeholder until NullableDate set
|
||||||
|
if (!element.NullableDate.HasValue)
|
||||||
|
{
|
||||||
|
Control.Text = element.PlaceHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == DatePicker.DateProperty.PropertyName ||
|
||||||
|
e.PropertyName == DatePicker.FormatProperty.PropertyName)
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedDatePicker element)
|
||||||
|
{
|
||||||
|
if (Element.Format == element.PlaceHolder)
|
||||||
|
{
|
||||||
|
Control.Text = element.PlaceHolder;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Android/Renderers/ExtendedGridRenderer.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public ExtendedGridRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(elementChangedEvent);
|
||||||
|
if (elementChangedEvent.NewElement != null)
|
||||||
|
{
|
||||||
|
SetBackgroundResource(Resource.Drawable.list_item_bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Android.Content;
|
|
||||||
using Android.Views;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Bit.Droid.Renderers;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(ExtendedListViewRenderer))]
|
|
||||||
namespace Bit.Droid.Renderers
|
|
||||||
{
|
|
||||||
public class ExtendedListViewRenderer : ListViewRenderer
|
|
||||||
{
|
|
||||||
public ExtendedListViewRenderer(Context context)
|
|
||||||
: base(context)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
|
||||||
{
|
|
||||||
base.OnElementChanged(e);
|
|
||||||
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
|
|
||||||
{
|
|
||||||
// Pad for FAB
|
|
||||||
Control.SetPadding(0, 0, 0, 170);
|
|
||||||
Control.SetClipToPadding(false);
|
|
||||||
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using AndroidX.Core.Content.Resources;
|
using AndroidX.Core.Content.Resources;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
@@ -18,6 +19,21 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == ExtendedSlider.ThumbBorderColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColor()
|
||||||
|
{
|
||||||
if (Control != null && Element is ExtendedSlider view)
|
if (Control != null && Element is ExtendedSlider view)
|
||||||
{
|
{
|
||||||
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
||||||
|
|||||||
23
src/Android/Renderers/ExtendedStackLayoutRenderer.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(elementChangedEvent);
|
||||||
|
if (elementChangedEvent.NewElement != null)
|
||||||
|
{
|
||||||
|
SetBackgroundResource(Resource.Drawable.list_item_bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/Android/Renderers/ExtendedStepperRenderer.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Graphics;
|
||||||
|
using Android.OS;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ExtendedStepper), typeof(ExtendedStepperRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedStepperRenderer : StepperRenderer
|
||||||
|
{
|
||||||
|
public ExtendedStepperRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
UpdateBgColor();
|
||||||
|
UpdateFgColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == ExtendedStepper.StepperBackgroundColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBgColor();
|
||||||
|
}
|
||||||
|
else if (e.PropertyName == ExtendedStepper.StepperForegroundColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateFgColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBgColor()
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedStepper view)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
|
||||||
|
{
|
||||||
|
Control.GetChildAt(0)?.Background?.SetColorFilter(
|
||||||
|
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
|
||||||
|
Control.GetChildAt(1)?.Background?.SetColorFilter(
|
||||||
|
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Control.GetChildAt(0)?.Background?.SetColorFilter(
|
||||||
|
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
|
||||||
|
Control.GetChildAt(1)?.Background?.SetColorFilter(
|
||||||
|
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFgColor()
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedStepper view)
|
||||||
|
{
|
||||||
|
var btn0 = Control.GetChildAt(0) as Android.Widget.Button;
|
||||||
|
btn0?.SetTextColor(view.StepperForegroundColor.ToAndroid());
|
||||||
|
var btn1 = Control.GetChildAt(1) as Android.Widget.Button;
|
||||||
|
btn1?.SetTextColor(view.StepperForegroundColor.ToAndroid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Android/Renderers/ExtendedTimePickerRenderer.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
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(ExtendedTimePicker), typeof(ExtendedTimePickerRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedTimePickerRenderer : TimePickerRenderer
|
||||||
|
{
|
||||||
|
public ExtendedTimePickerRenderer(Context context)
|
||||||
|
: base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
if (Control != null && Element is ExtendedTimePicker element)
|
||||||
|
{
|
||||||
|
// center text
|
||||||
|
Control.Gravity = GravityFlags.CenterHorizontal;
|
||||||
|
|
||||||
|
// use placeholder until NullableTime set
|
||||||
|
if (!element.NullableTime.HasValue)
|
||||||
|
{
|
||||||
|
Control.Text = element.PlaceHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == TimePicker.TimeProperty.PropertyName ||
|
||||||
|
e.PropertyName == TimePicker.FormatProperty.PropertyName)
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedTimePicker element)
|
||||||
|
{
|
||||||
|
if (Element.Format == element.PlaceHolder)
|
||||||
|
{
|
||||||
|
Control.Text = element.PlaceHolder;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Android/Renderers/SelectableLabelRenderer.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(SelectableLabel), typeof(SelectableLabelRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class SelectableLabelRenderer : LabelRenderer
|
||||||
|
{
|
||||||
|
public SelectableLabelRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
Control.SetTextIsSelectable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 775 B |
|
Before Width: | Height: | Size: 487 B |
|
Before Width: | Height: | Size: 581 B |
|
Before Width: | Height: | Size: 904 B |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 943 B |
|
Before Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 635 B |
|
Before Width: | Height: | Size: 440 B |
15
src/Android/Resources/drawable-v23/splash_screen.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
TODO: When API 23 becomes our new minimum, replace 'splash_screen.xml' in 'drawable' with this version, and delete
|
||||||
|
all versions of 'logo_legacy.png' and 'logo_white_legacy.png'. The bitmaps only exist due to a bug in API <23 where
|
||||||
|
vector drawables used in layer-lists (our splash screen) stretch to fill the full height of the parent.
|
||||||
|
-->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<color android:color="@color/white"/>
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/logo"
|
||||||
|
android:tileMode="disabled"
|
||||||
|
android:gravity="center"/>
|
||||||
|
</layer-list>
|
||||||
15
src/Android/Resources/drawable-v23/splash_screen_dark.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
TODO: When API 23 becomes our new minimum, replace 'splash_screen_dark.xml' in 'drawable' with this version, and delete
|
||||||
|
all versions of 'logo_legacy.png' and 'logo_white_legacy.png'. The bitmaps only exist due to a bug in API <23 where
|
||||||
|
vector drawables used in layer-lists (our splash screen) stretch to fill the full height of the parent.
|
||||||
|
-->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<color android:color="@color/darkgray"/>
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/logo_white"
|
||||||
|
android:tileMode="disabled"
|
||||||
|
android:gravity="center"/>
|
||||||
|
</layer-list>
|
||||||
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 695 B |
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
src/Android/Resources/drawable-xhdpi/logo_legacy.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/Android/Resources/drawable-xhdpi/logo_white_legacy.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 359 B |
|
Before Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 817 B |
|
Before Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 24 KiB |