Compare commits
399 commits
try_zxing_
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
6cdd7ce4f7 | ||
|
38ca9d7911 | ||
|
5a19e83a3c | ||
|
4088486344 | ||
|
714ffd0ea1 | ||
|
0e415479f7 | ||
|
3339d318fe | ||
|
2a8b39ce6b | ||
|
105b3195a2 | ||
|
f0485d7865 | ||
|
6c5298b071 | ||
|
5fb07e0508 | ||
|
2831f60c30 | ||
|
b98155e48b | ||
|
a61bae906e | ||
|
96cf096f89 | ||
|
1b6353d470 | ||
|
dd187ab69f | ||
|
a6f7d4699f | ||
|
19372eaa27 | ||
|
c3a5912ff0 | ||
|
85a4501d12 | ||
|
b5052184b2 | ||
|
ba931c787f | ||
|
4e3da926f6 | ||
|
5a6c43fe76 | ||
|
af4c42033c | ||
|
7fc5c58d98 | ||
|
bf6c0226ff | ||
|
0b4aad772b | ||
|
4e62614ef9 | ||
|
1da68a5706 | ||
|
ec80dced3d | ||
|
dcb8fb6eff | ||
|
b47efaf943 | ||
|
a759829497 | ||
|
a7b1acb57b | ||
|
9d6025546f | ||
|
ed999afcd4 | ||
|
5f6b6b2e6d | ||
|
a10ea1330f | ||
|
977c341f0e | ||
|
8a55eafff2 | ||
|
4af1c28b1c | ||
|
0d206f62bc | ||
|
38f0706855 | ||
|
5865182001 | ||
|
b4b3eca448 | ||
|
16b4b05db0 | ||
|
f3738027b2 | ||
|
24f6344663 | ||
|
5b1fb49f60 | ||
|
ac6c67ae0e | ||
|
2b44c6d831 | ||
|
fa45294f21 | ||
|
95232ec455 | ||
|
4374e819d7 | ||
|
ae92eeab70 | ||
|
dfdc8e6c09 | ||
|
7cb399d72c | ||
|
97d3fd8f40 | ||
|
532ede4b17 | ||
|
c3c3ae1951 | ||
|
d624383d91 | ||
|
e853941de9 | ||
|
b3eeda7cff | ||
|
d6d276e349 | ||
|
48d2a945cb | ||
|
8fa742aeab | ||
|
ae92d78443 | ||
|
8e636ee501 | ||
|
1ec8d120a0 | ||
|
98dc2ae8ef | ||
|
1ec753222d | ||
|
488d1be731 | ||
|
c7460138c4 | ||
|
ab41c587ab | ||
|
4efab4213e | ||
|
e187c5999a | ||
|
6ad1049351 | ||
|
b9082a97cd | ||
|
e3a93a8010 | ||
|
c0196680a0 | ||
|
1c8c5a254f | ||
|
ac611db5f8 | ||
|
1a8360417c | ||
|
1a7b8630fb | ||
|
a4e710d804 | ||
|
15d8514b6c | ||
|
7a7abe61c2 | ||
|
9a7a07e7d4 | ||
|
04b745705d | ||
|
16bcb956db | ||
|
6f78caac2a | ||
|
d7c2558980 | ||
|
61038ec0e5 | ||
|
9deb2402e9 | ||
|
3cbb0af0c6 | ||
|
a6baee2ca2 | ||
|
b95ff6d63a | ||
|
f6f6d105d0 | ||
|
dd5eeebc3f | ||
|
acb8f345e2 | ||
|
9bd99a8f73 | ||
|
a553a34cca | ||
|
b71e8ca747 | ||
|
c1850d10c9 | ||
|
536fd4faf1 | ||
|
dab0d7e48f | ||
|
ad2c4ffa25 | ||
|
6f6de55235 | ||
|
9d2300bd0a | ||
|
bfcbe4446e | ||
|
bf38f5f289 | ||
|
1f86ea002f | ||
|
6ceb648bf4 | ||
|
ea1d39b704 | ||
|
7ddf457732 | ||
|
85a97f0dcf | ||
|
99fb620a5b | ||
|
ad752cc251 | ||
|
1d2a413426 | ||
|
fcd9ba26c2 | ||
|
9dbc837430 | ||
|
64ec30d29b | ||
|
9756e696b3 | ||
|
6830623480 | ||
|
a3d8bbc572 | ||
|
eac62d8bd8 | ||
|
9923af6819 | ||
|
7aba6e61de | ||
|
d26478499c | ||
|
e049f2721e | ||
|
492232f22d | ||
|
8596289d9f | ||
|
2d50ff49b8 | ||
|
dede322da3 | ||
|
d39c0d1cbc | ||
|
346d71f434 | ||
|
e51369af86 | ||
|
732b86ebca | ||
|
d0be7e6815 | ||
|
e01ebf5230 | ||
|
4c06188b36 | ||
|
1066dd34d5 | ||
|
328bd4bd4b | ||
|
6f37e4d78f | ||
|
9729d9da63 | ||
|
f6fdf504c9 | ||
|
9eb7362b3a | ||
|
1ffa7fd49d | ||
|
0ae2b1712e | ||
|
63a06fcad0 | ||
|
173596098e | ||
|
6049192d2f | ||
|
4678da2a10 | ||
|
92bcc806e4 | ||
|
d7e25ad1fd | ||
|
7d5babafef | ||
|
5ddc55ac40 | ||
|
d5b326dab9 | ||
|
d89b7f8157 | ||
|
0e76e775fa | ||
|
55b8124853 | ||
|
4a6db0f8b3 | ||
|
e02af2ef81 | ||
|
84ecb71b31 | ||
|
9e73a8d6d9 | ||
|
908181487e | ||
|
ddbaff13dc | ||
|
57631eb18e | ||
|
99aa0df767 | ||
|
8002130a38 | ||
|
08359f6bc7 | ||
|
dcdca77fc6 | ||
|
bbfc09ba1d | ||
|
d5fbdc8b80 | ||
|
a7eb22ce2c | ||
|
3e08791324 | ||
|
c3d27e4eb1 | ||
|
9d180cf455 | ||
|
c5f32b275d | ||
|
704503b9f5 | ||
|
b2727019c5 | ||
|
954f745a0c | ||
|
65ee6ca578 | ||
|
70391dd8fe | ||
|
d5b30fe405 | ||
|
db0da8a178 | ||
|
be904d4848 | ||
|
e686a338c3 | ||
|
6e00aaeb55 | ||
|
c286a6837a | ||
|
417d982115 | ||
|
b92ae21903 | ||
|
553115fce1 | ||
|
a947a296ec | ||
|
6405ff1015 | ||
|
0d766598ab | ||
|
b9ada8fde3 | ||
|
174eaef9dd | ||
|
2eae832bc0 | ||
|
601f16eca1 | ||
|
00e740da91 | ||
|
6ad554616b | ||
|
74fe5540a4 | ||
|
923545fd06 | ||
|
b3c29e2b8a | ||
|
5ca84140bd | ||
|
8b020075e0 | ||
|
b57b51f609 | ||
|
d4dee029de | ||
|
90e1c75580 | ||
|
1207e95f43 | ||
|
ac8a7339cd | ||
|
b9ca1ce816 | ||
|
c831d5cb3b | ||
|
91cc5b7ff9 | ||
|
c6185b29db | ||
|
222c2df562 | ||
|
61c40524cb | ||
|
f4f74566ef | ||
|
01622c9643 | ||
|
9aa2a6d41d | ||
|
59dcba0831 | ||
|
d582b40160 | ||
|
91e350cb2e | ||
|
7e90275433 | ||
|
d806076fe7 | ||
|
2f182665d1 | ||
|
5f6017b169 | ||
|
54a1c96eff | ||
|
e76700a589 | ||
|
a95ba652c6 | ||
|
897e298764 | ||
|
21e167c126 | ||
|
3a9d192ba7 | ||
|
1f4ac8bd7b | ||
|
bc8cc0283b | ||
|
4858b36a4a | ||
|
dc4073d372 | ||
|
76a71dd87c | ||
|
0b7ab6dfe0 | ||
|
245e8b3ae9 | ||
|
b150886b9b | ||
|
f431b97deb | ||
|
1e674dce3a | ||
|
b8a4974744 | ||
|
16e0c26f45 | ||
|
be3d15f2b8 | ||
|
2680f5f05d | ||
|
388bc137e1 | ||
|
9524c63ce2 | ||
|
84017d57c3 | ||
|
429531c624 | ||
|
7398f69149 | ||
|
361fda7b75 | ||
|
e94ef98332 | ||
|
77a96bf8dc | ||
|
7ae9e9ac5a | ||
|
c67a2a8269 | ||
|
fb399f6dae | ||
|
b99fe8a359 | ||
|
480868c699 | ||
|
413df5f2ef | ||
|
eea18f66cd | ||
|
387e5f76bd | ||
|
79f6a5ae81 | ||
|
553a2de631 | ||
|
f097738630 | ||
|
02fe60bdfd | ||
|
c48b84cfd3 | ||
|
8f00b82268 | ||
|
8b796d9efe | ||
|
d4455fff68 | ||
|
04a25a1169 | ||
|
b9b9920a91 | ||
|
48a66190c4 | ||
|
085fc750fb | ||
|
288c85ae68 | ||
|
7e2526a20a | ||
|
46b91f040c | ||
|
c8fff3735a | ||
|
f364aad0ba | ||
|
847b19feac | ||
|
ce4030ac5d | ||
|
9da9f7b179 | ||
|
e7b959acc7 | ||
|
2fb1eeee28 | ||
|
9d3ed8b5df | ||
|
1361bb6fc4 | ||
|
3c873c2bd1 | ||
|
e476be2011 | ||
|
de281b6f15 | ||
|
2a37166d4c | ||
|
deaff19be7 | ||
|
89e1512163 | ||
|
512049ea54 | ||
|
7b38bdb40d | ||
|
b72d8d42c8 | ||
|
07d505dc89 | ||
|
b30af764fb | ||
|
bf00ea895a | ||
|
b92041b13a | ||
|
5953756578 | ||
|
6a2fccbb16 | ||
|
4dfaac8af7 | ||
|
520b66aab7 | ||
|
c4d23e366d | ||
|
9c109c9efc | ||
|
b496d0e477 | ||
|
582a1519bf | ||
|
9348ad8f15 | ||
|
45a436a3b2 | ||
|
c313553b13 | ||
|
a4794ee587 | ||
|
c604fca0ac | ||
|
b87094a544 | ||
|
8ac7969093 | ||
|
39c10520d3 | ||
|
e869a61a33 | ||
|
28a806fd52 | ||
|
2ad3cb5402 | ||
|
67727e9159 | ||
|
3b83081419 | ||
|
fda0afd2e3 | ||
|
15a9743769 | ||
|
6e9df603e8 | ||
|
2bb0342ed2 | ||
|
9f0b9a37c5 | ||
|
7cc2d83825 | ||
|
1f19a2c6c7 | ||
|
9310188f6b | ||
|
5fcdd516c8 | ||
|
f4feb0c3ca | ||
|
511e749ca2 | ||
|
6389046047 | ||
|
4f655a4d05 | ||
|
df5e987683 | ||
|
001326c40d | ||
|
8bca0eb83a | ||
|
e411c45c86 | ||
|
630897d68f | ||
|
eabd7d4d7d | ||
|
8d89438905 | ||
|
a7a42420f8 | ||
|
c19357000e | ||
|
7299e74d58 | ||
|
76d7e86c7c | ||
|
7638a95791 | ||
|
1cf27c4737 | ||
|
4444d21d0f | ||
|
deb6fe8e95 | ||
|
1f8cac4d98 | ||
|
bfb9c03dfe | ||
|
6652b5166c | ||
|
b814c21ec1 | ||
|
77960c92e6 | ||
|
2ec15648ce | ||
|
227cba6667 | ||
|
7273da05cb | ||
|
6a7c45e8ae | ||
|
78a2d63be3 | ||
|
572da6eafe | ||
|
5c2a1e743e | ||
|
6f969f4217 | ||
|
74d4fbe409 | ||
|
098e6bcc9f | ||
|
949e312a0f | ||
|
3a11b6ae4e | ||
|
cc02abf014 | ||
|
7c461160d3 | ||
|
b1e3bbd241 | ||
|
42242cdaf5 | ||
|
10d13bbd32 | ||
|
05b44cadd3 | ||
|
cc8b3fb61f | ||
|
b307fe61df | ||
|
4e3ccc6c16 | ||
|
09a34451ff | ||
|
3c377a4683 | ||
|
a129422401 | ||
|
15786c8a78 | ||
|
89297a2736 | ||
|
13586cc08a | ||
|
296d9211b2 | ||
|
eae71f6d47 | ||
|
43bbfcc4b0 | ||
|
beb20e782e | ||
|
a479539911 | ||
|
e4e49f89c4 | ||
|
f76430f474 | ||
|
64add36331 | ||
|
ab0d316fd3 | ||
|
883bd14309 | ||
|
7deec6c3b2 | ||
|
0e8550989c | ||
|
945a85c76c | ||
|
d15c64d19e |
329 changed files with 8632 additions and 7174 deletions
22
.ci/kontinuum.json
Normal file
22
.ci/kontinuum.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "android",
|
||||||
|
"stages": [
|
||||||
|
{
|
||||||
|
"name": "testWithMapsWithAnalyticsForPlayDebugComposer",
|
||||||
|
"needsEmulator": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lint",
|
||||||
|
"needsEmulator": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"needsEmulator": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assembleRelease",
|
||||||
|
"needsEmulator": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: ligi
|
||||||
|
patreon: ligi
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
Great that you are thinking about submitting a PullRequest!
|
Great that you are thinking about submitting a PullRequest!
|
||||||
|
|
||||||
|
|
||||||
Please keep them small if not otherwise possible. Contact me *before* you are creating a big PR to avoid unnecessary work and rebasing of big PRs. Also try to add tests - I am not dogmatic about that but prefer PRs backed by tests. This project has everything setup for Espresso UI and Unit-tests. Also the existing unit and UI-tests muss pass before submitting a PullRequest.
|
Please keep them small if not otherwise possible. Contact me *before* you are creating a big PR to avoid unnecessary work and rebasing of big PRs. Also try to add tests - I am not dogmatic about that but prefer PRs backed by tests. This project has everything setup for Espresso UI and Unit-tests. Also the existing unit and UI-tests muss pass before submitting a PullRequest.
|
||||||
|
|
||||||
Please base the PullRequests on the branch named dev
|
Please base the PullRequests on the branch named dev if one currently exists - otherwise use master
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,7 +5,6 @@ build
|
||||||
bin
|
bin
|
||||||
gen
|
gen
|
||||||
project.properties
|
project.properties
|
||||||
gradle.properties
|
|
||||||
local.properties
|
local.properties
|
||||||
*iml
|
*iml
|
||||||
/build.gradle
|
|
||||||
|
|
12
Jenkinsfile
vendored
12
Jenkinsfile
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
node {
|
node {
|
||||||
def flavorCombination='WithMapsWithAnalyticsForPlay'
|
def flavorCombination='WithMapsWithAnalyticsForPlay'
|
||||||
|
|
||||||
|
@ -11,8 +12,8 @@ node {
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
currentBuild.result = FAILURE
|
currentBuild.result = FAILURE
|
||||||
} finally {
|
} finally {
|
||||||
publishHTML(target:[allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "android/build/spoon-output/${flavorCombination}DebugAndroidTest", reportFiles: 'index.html', reportName: 'Spoon'])
|
publishHTML(target:[allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "android/build/spoon", reportFiles: '*/debug/index.html', reportName: 'Spoon'])
|
||||||
step([$class: 'JUnitResultArchiver', testResults: 'android/build/spoon-output/*/junit-reports/*.xml'])
|
step([$class: 'JUnitResultArchiver', testResults: 'android/build/spoon/*/debug/junit-reports/*.xml'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +23,8 @@ node {
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
currentBuild.result = FAILURE
|
currentBuild.result = FAILURE
|
||||||
} finally {
|
} finally {
|
||||||
publishHTML(target:[allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'android/build/outputs/', reportFiles: "lint-results-*Release.html", reportName: 'Lint'])
|
|
||||||
androidLint canComputeNew: false, defaultEncoding: '', healthy: '', pattern: '', unHealthy: ''
|
androidLint canComputeNew: false, defaultEncoding: '', healthy: '', pattern: '', unHealthy: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
stage 'test'
|
stage 'test'
|
||||||
try {
|
try {
|
||||||
|
@ -32,8 +32,8 @@ node {
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
currentBuild.result = FAILURE
|
currentBuild.result = FAILURE
|
||||||
} finally {
|
} finally {
|
||||||
step([$class: 'JUnitResultArchiver', testResults: 'android/build/test-results/*/*/*.xml'])
|
step([$class: 'JUnitResultArchiver', testResults: 'android/build/test-results/*/*.xml'])
|
||||||
publishHTML(target:[allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'android/build/reports/tests/', reportFiles: "*/*/index.html", reportName: 'UnitTest'])
|
publishHTML(target:[allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'android/build/reports/tests/', reportFiles: "*/index.html", reportName: 'UnitTest'])
|
||||||
}
|
}
|
||||||
|
|
||||||
stage 'assemble'
|
stage 'assemble'
|
||||||
|
|
29
README.md
29
README.md
|
@ -1,15 +1,24 @@
|
||||||
[![Android app on Google Play](http://ligi.de/img/play_badge.png)](https://play.google.com/store/apps/details?id=org.ligi.passandroid)
|
[![on Google Play](https://ligi.de/img/play_badge.png)](https://play.google.com/store/apps/details?id=org.ligi.passandroid)
|
||||||
[![Android app on FDroid](http://ligi.de/img/fdroid_badge.png)](https://f-droid.org/repository/browse/?fdid=org.ligi.passandroid)
|
[![on FDroid](https://ligi.de/img/fdroid_badge.png)](https://f-droid.org/repository/browse/?fdid=org.ligi.passandroid)
|
||||||
|
[![on Amazon](https://ligi.de/img/amazon_badge.png)](https://www.amazon.com/ligi-Passandroid/dp/B01LX9DMSQ)
|
||||||
|
|
||||||
PassAndroid
|
# PassAndroid
|
||||||
===========
|
|
||||||
|
|
||||||
Android App to view Passes ( e.g. event tickets, coupons, loyalty cards, boarding passes, .. )
|
Android App to view Passes (e.g. event tickets, coupons, loyalty cards, boarding passes, ...)
|
||||||
|
|
||||||
<img src="http://ligi.de/img/passandroid_screenshots.png"/>
|
![Screenshots](https://ligi.de/img/passandroid_screenshots.png)
|
||||||
|
|
||||||
Displays [esPass (*.esPass)](http://espass.it) and Passbook ( *.pkpass ) files & shows the Barcode ( QR, PDF417, AZTEC, Code 39 and Code 128 format ). It useable offline.
|
Displays [esPass](https://espass.it) (`*.esPass`) & Passbook (`*.pkpass`) files, shows the Barcode (QR, PDF417, AZTEC, Code 39 and Code 128 format) and is also usable offline.
|
||||||
When preparing for the Chaos Communication Congress 2012 ( #29c3 ) I stumbled upon a passbook file for the first time. As I really like the idea of paperless tickets as it saves time and trees which both are very valuable to me. The problem was that I found no app with which I could use the downloaded passbook file.
|
|
||||||
|
When preparing for the Chaos Communication Congress 2012 #29c3 I stumbled upon a passbook file for the first time.
|
||||||
|
I really like the idea of paperless tickets as it saves time and trees which are both very valuable to me.
|
||||||
|
The problem was that I was unable to find an app to open and use the downloaded passbook files with, that's why I wrote my own one.
|
||||||
|
|
||||||
|
## Legal
|
||||||
|
|
||||||
|
This project is licensed under the [GNU General Public License v3.0](COPYING).
|
||||||
|
|
||||||
|
We are not affiliated with Apple - Passbook might be trademarked by Apple, but it's introduced like a standard so this should be okay.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
@ -17,7 +26,3 @@ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
Status
|
|
||||||
------
|
|
||||||
|
|
||||||
[![Build Status](https://snap-ci.com/ligi/PassAndroid/branch/master/build_image)](https://snap-ci.com/ligi/PassAndroid/branch/master)
|
|
||||||
|
|
|
@ -1,56 +1,29 @@
|
||||||
buildscript {
|
|
||||||
ext {
|
|
||||||
support_version = '24.2.1'
|
|
||||||
dagger_version = '2.7'
|
|
||||||
kotlin_version = '1.0.4'
|
|
||||||
|
|
||||||
butterknife_version = '8.4.0'
|
|
||||||
play_version = '9.6.1'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
maven { url 'https://jitpack.io' }
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
classpath 'com.android.tools.build:gradle:2.2.1'
|
|
||||||
classpath 'de.felixschulze.gradle:gradle-spoon-plugin:2.7.3'
|
|
||||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
|
|
||||||
classpath 'de.mobilej.unmock:UnMockPlugin:0.5.0'
|
|
||||||
|
|
||||||
// http://stackoverflow.com/a/33889117/322642
|
|
||||||
classpath 'com.github.JakeWharton:sdk-manager-plugin:220bf7a88a7072df3ed16dc8466fb144f2817070'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'android-sdk-manager'
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'de.felixschulze.gradle.spoon'
|
apply plugin: 'com.trevjonez.composer'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'de.mobilej.unmock'
|
apply plugin: 'de.mobilej.unmock'
|
||||||
apply plugin: 'com.github.ben-manes.versions'
|
apply plugin: 'com.github.triplet.play'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven { url 'https://jitpack.io' }
|
google()
|
||||||
|
maven { url 'https://www.jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdkVersion 24
|
compileSdkVersion 29
|
||||||
buildToolsVersion "24.0.2"
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode 326
|
versionCode 356
|
||||||
versionName "3.2.6"
|
versionName "3.5.6"
|
||||||
minSdkVersion 9
|
minSdkVersion 14
|
||||||
targetSdkVersion 24
|
targetSdkVersion 29
|
||||||
applicationId "org.ligi.passandroid"
|
applicationId "org.ligi.passandroid"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "org.ligi.passandroid.AppReplacingRunner"
|
||||||
archivesBaseName = "PassAndroid-$versionName"
|
archivesBaseName = "PassAndroid-$versionName"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
@ -82,6 +55,22 @@ android {
|
||||||
forPlay {
|
forPlay {
|
||||||
dimension "distribution"
|
dimension "distribution"
|
||||||
}
|
}
|
||||||
|
forAmazon {
|
||||||
|
dimension "distribution"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
android.variantFilter { variant ->
|
||||||
|
def maps = variant.getFlavors().get(0).name
|
||||||
|
def analytics = variant.getFlavors().get(1).name
|
||||||
|
def distribution = variant.getFlavors().get(2).name
|
||||||
|
|
||||||
|
variant.setIgnore((project.hasProperty("singleFlavor") && (distribution != 'forPlay'))
|
||||||
|
|| ((distribution == 'forAmazon' || distribution == 'forPlay') && analytics == 'noAnalytics')
|
||||||
|
|| ((distribution == 'forAmazon' || distribution == 'forPlay') && maps == 'noMaps')
|
||||||
|
|| (distribution == 'forFDroid' && analytics == 'withAnalytics')
|
||||||
|
|| (distribution == 'forFDroid' && maps == 'withMaps'))
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
|
@ -95,15 +84,11 @@ android {
|
||||||
|
|
||||||
exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
|
exclude 'META-INF/maven/com.google.guava/guava/pom.properties'
|
||||||
exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
|
exclude 'META-INF/maven/com.google.guava/guava/pom.xml'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
warning 'MissingTranslation'
|
warning 'MissingTranslation'
|
||||||
warning 'InvalidPackage'
|
warning 'InvalidPackage'
|
||||||
|
|
||||||
// TODO: remove when this is solved: http://stackoverflow.com/questions/39714487/kapt2-and-butterknife-produce-lint-error-expected-resource-of-type-id
|
|
||||||
warning 'ResourceType'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -112,98 +97,99 @@ android {
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
multiDexEnabled true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
kapt "com.google.dagger:dagger-compiler:$dagger_version"
|
implementation 'org.permissionsdispatcher:permissionsdispatcher:4.6.0'
|
||||||
kapt "com.jakewharton:butterknife-compiler:$butterknife_version"
|
kapt 'org.permissionsdispatcher:permissionsdispatcher-processor:4.6.0'
|
||||||
|
|
||||||
provided 'org.glassfish:javax.annotation:10.0-b28'
|
implementation "org.koin:koin-android:2.1.2"
|
||||||
|
|
||||||
kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
|
||||||
androidTestCompile "com.android.support:support-annotations:$support_version"
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
|
||||||
|
|
||||||
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
|
androidTestImplementation 'com.github.ligi:trulesk:0.31'
|
||||||
// http://stackoverflow.com/questions/30578243/why-would-adding-espresso-contrib-cause-an-inflateexception
|
androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.1.0'
|
||||||
exclude group: 'com.android.support', module: 'appcompat'
|
|
||||||
exclude group: 'com.android.support', module: 'support-v4'
|
|
||||||
exclude group: 'javax.inject'
|
|
||||||
exclude module: 'recyclerview-v7'
|
|
||||||
}
|
|
||||||
|
|
||||||
androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') {
|
androidTestImplementation 'androidx.test:core:1.2.0'
|
||||||
// http://stackoverflow.com/questions/30578243/why-would-adding-espresso-contrib-cause-an-inflateexception
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
exclude group: 'javax.inject'
|
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
|
||||||
}
|
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
|
||||||
|
androidTestImplementation 'com.squareup.assertj:assertj-android:1.2.0'
|
||||||
|
androidTestImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
|
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:2.25.1'
|
||||||
|
androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
androidTestImplementation 'org.threeten:threetenbp:1.4.1'
|
||||||
|
androidTestImplementation 'com.android.support:multidex:1.0.3'
|
||||||
|
|
||||||
androidTestCompile 'com.squareup.spoon:spoon-client:1.7.0'
|
|
||||||
androidTestCompile 'com.squareup.assertj:assertj-android:1.1.1'
|
|
||||||
|
|
||||||
androidTestCompile 'org.mockito:mockito-core:1.9.5'
|
implementation 'com.github.ligi:TouchImageView:2.1'
|
||||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
|
implementation 'com.github.ligi:ExtraCompats:1.0'
|
||||||
|
implementation 'net.lingala.zip4j:zip4j:2.3.2'
|
||||||
|
implementation 'com.jakewharton.threetenabp:threetenabp:1.2.2'
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
androidTestCompile 'com.jraska:falcon-spoon-compat:1.0.3'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
implementation 'androidx.annotation:annotation:1.1.0'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
|
implementation 'net.i2p.android.ext:floatingactionbutton:1.10.1'
|
||||||
|
|
||||||
compile 'net.lingala.zip4j:zip4j:1.3.2'
|
implementation 'com.github.ligi:KAXT:1.0'
|
||||||
compile "com.jakewharton:butterknife:$butterknife_version"
|
implementation 'com.github.ligi:KAXTUI:1.0'
|
||||||
compile 'com.jakewharton.threetenabp:threetenabp:1.0.4'
|
implementation 'com.github.ligi:loadtoast:1.10.11'
|
||||||
compile 'org.greenrobot:eventbus:3.0.0'
|
implementation 'com.github.ligi:tracedroid:3.0'
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
forPlayImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.24'
|
||||||
compile "org.jetbrains.kotlin:kotlin-runtime:$kotlin_version"
|
forFDroidImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.24'
|
||||||
|
forAmazonImplementation 'com.github.ligi.snackengage:snackengage-amazonrate:0.24'
|
||||||
|
|
||||||
compile "com.android.support:support-annotations:$support_version"
|
// https://medium.com/square-corner-blog/okhttp-3-13-requires-android-5-818bb78d07ce
|
||||||
compile "com.android.support:recyclerview-v7:$support_version"
|
// Don't update to >=3.13 before minSDK 21 + Java 8
|
||||||
compile "com.android.support:appcompat-v7:$support_version"
|
//noinspection GradleDependency
|
||||||
compile "com.android.support:cardview-v7:$support_version"
|
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
|
||||||
compile "com.android.support:design:$support_version"
|
|
||||||
compile "com.android.support:preference-v7:$support_version"
|
|
||||||
|
|
||||||
androidTestCompile "com.android.support:appcompat-v7:$support_version"
|
implementation 'com.larswerkman:HoloColorPicker:1.5'
|
||||||
androidTestCompile "com.android.support:design:$support_version"
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
|
||||||
compile 'net.i2p.android.ext:floatingactionbutton:1.10.1'
|
implementation 'com.squareup.okio:okio:2.2.2'
|
||||||
|
|
||||||
|
implementation 'com.squareup.moshi:moshi:1.9.2'
|
||||||
|
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.9.2")
|
||||||
|
|
||||||
compile 'org.ligi:AXT:0.37'
|
implementation 'com.chibatching.kotpref:kotpref:2.10.0'
|
||||||
compile 'org.ligi:tracedroid:1.4'
|
implementation 'com.chibatching.kotpref:initializer:2.10.0'
|
||||||
compile 'com.github.ligi:snackengage:0.8'
|
testImplementation 'androidx.annotation:annotation:1.1.0'
|
||||||
compile 'com.squareup.okhttp3:okhttp:3.4.1'
|
testImplementation 'com.squareup.assertj:assertj-android:1.2.0'
|
||||||
compile 'com.larswerkman:HoloColorPicker:1.5'
|
testImplementation 'junit:junit:4.12'
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
compile 'com.squareup.moshi:moshi:1.2.0'
|
testImplementation 'org.threeten:threetenbp:1.4.1'
|
||||||
compile 'com.chibatching:kotpref:1.4.0'
|
|
||||||
|
|
||||||
compile 'net.steamcrafted:load-toast:1.0.10'
|
// https://github.com/ligi/PassAndroid/issues/181
|
||||||
|
// Don't upgrade before minSDK 19 - or replace zxing
|
||||||
|
//noinspection GradleDependency
|
||||||
|
implementation 'com.google.zxing:core:3.3.0'
|
||||||
|
|
||||||
compile "com.google.dagger:dagger:$dagger_version"
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||||
|
|
||||||
androidTestCompile 'com.google.code.findbugs:jsr305:3.0.1'
|
// requires minSDK 16 according to docs, not sure if it causes an issue
|
||||||
|
withAnalyticsImplementation 'com.google.android.gms:play-services-analytics:17.0.0'
|
||||||
testCompile "com.android.support:support-annotations:$support_version"
|
withMapsImplementation 'com.google.android.gms:play-services-maps:17.0.0'
|
||||||
testCompile 'com.squareup.assertj:assertj-android:1.1.1'
|
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
testCompile 'org.mockito:mockito-core:1.9.5'
|
|
||||||
testCompile 'org.threeten:threetenbp:1.3.2'
|
|
||||||
|
|
||||||
// cannot use this new version: https://github.com/zxing/zxing/issues/165
|
|
||||||
// WARNING: might work on some devices or the emulator - but fails on others
|
|
||||||
compile 'com.google.zxing:core:3.3.0'
|
|
||||||
|
|
||||||
//compile fileTree(dir: 'libs', include: 'zxing-core-2.3.0-SNAPSHOT.jar')
|
|
||||||
|
|
||||||
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4'
|
|
||||||
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4'
|
|
||||||
|
|
||||||
withAnalyticsCompile "com.google.android.gms:play-services-analytics:$play_version"
|
|
||||||
withMapsCompile "com.google.android.gms:play-services-maps:$play_version"
|
|
||||||
|
|
||||||
compile 'com.github.hotchemi:permissionsdispatcher:2.2.0'
|
|
||||||
kapt 'com.github.hotchemi:permissionsdispatcher-processor:2.2.0'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spoon {
|
play {
|
||||||
debug = true
|
jsonFile = file('/media/ligi/USBCRED/play.json')
|
||||||
}
|
uploadImages = true
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
lombok.addGeneratedAnnotation = false
|
|
|
@ -20,31 +20,24 @@
|
||||||
# If your project uses WebView with JS, uncomment the following
|
# If your project uses WebView with JS, uncomment the following
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
# class:
|
# class:
|
||||||
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
public *;
|
# public *;
|
||||||
}
|
#}
|
||||||
|
|
||||||
# optimize
|
# optimize
|
||||||
-optimizationpasses 2
|
-optimizationpasses 2
|
||||||
-optimizations !code/simplification/arithmetic
|
-optimizations !code/simplification/arithmetic
|
||||||
-dontusemixedcaseclassnames
|
-dontusemixedcaseclassnames
|
||||||
-dontskipnonpubliclibraryclasses
|
-dontskipnonpubliclibraryclasses
|
||||||
|
|
||||||
# AppCompat
|
# Keep line numbers to alleviate debugging stack traces
|
||||||
|
|
||||||
-dontwarn android.support.v7.**
|
-renamesourcefileattribute SourceFile
|
||||||
-keep class android.support.v7.** { *; }
|
|
||||||
-keep interface android.support.v7.** { *; }
|
|
||||||
|
|
||||||
# Keep line numbers to alleviate debugging stack traces
|
|
||||||
|
|
||||||
-renamesourcefileattribute SourceFile
|
|
||||||
|
|
||||||
-keepattributes SourceFile,LineNumberTable
|
-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
### for api client
|
### for api client
|
||||||
|
|
||||||
|
|
||||||
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
|
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
|
||||||
-keepclassmembers class * {
|
-keepclassmembers class * {
|
||||||
|
@ -54,7 +47,6 @@
|
||||||
# Needed by Guava
|
# Needed by Guava
|
||||||
# See https://groups.google.com/forum/#!topic/guava-discuss/YCZzeCiIVoI
|
# See https://groups.google.com/forum/#!topic/guava-discuss/YCZzeCiIVoI
|
||||||
|
|
||||||
|
|
||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-dontwarn com.google.common.collect.MinMaxPriorityQueue
|
-dontwarn com.google.common.collect.MinMaxPriorityQueue
|
||||||
|
|
||||||
|
@ -72,9 +64,6 @@
|
||||||
-keep class **$$ViewBinder { *; }
|
-keep class **$$ViewBinder { *; }
|
||||||
-keepnames class * { @butterknife.Bind *;}
|
-keepnames class * { @butterknife.Bind *;}
|
||||||
|
|
||||||
#### for support 22
|
|
||||||
-dontwarn android.support.**
|
|
||||||
|
|
||||||
#### for guava
|
#### for guava
|
||||||
-dontwarn javax.annotation.**
|
-dontwarn javax.annotation.**
|
||||||
-dontwarn javax.inject.**
|
-dontwarn javax.inject.**
|
||||||
|
@ -105,23 +94,20 @@
|
||||||
-dontwarn java.nio.file.OpenOption
|
-dontwarn java.nio.file.OpenOption
|
||||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
|
||||||
-keep public class com.google.android.gms.**
|
|
||||||
-dontwarn com.google.android.gms.**
|
|
||||||
|
|
||||||
|
|
||||||
## New rules for EventBus 3.0.x ##
|
## New rules for EventBus 3.0.x ##
|
||||||
# http://greenrobot.org/eventbus/documentation/proguard/
|
# http://greenrobot.org/eventbus/documentation/proguard/
|
||||||
|
|
||||||
-keepattributes *Annotation*
|
-keepattributes *Annotation*
|
||||||
-keepclassmembers class ** {
|
-keepclassmembers class * {
|
||||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||||
}
|
}
|
||||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||||
|
|
||||||
# Only required if you use AsyncExecutor
|
# Only required if you use AsyncExecutor
|
||||||
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
||||||
<init>(java.lang.Throwable);
|
<init>(java.lang.Throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
### for moshi
|
### for moshi
|
||||||
|
|
||||||
|
@ -153,3 +139,17 @@
|
||||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||||
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
|
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## okhttp
|
||||||
|
|
||||||
|
# JSR 305 annotations are for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||||
|
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||||
|
|
||||||
|
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||||
|
|
||||||
|
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
|
||||||
|
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
|
||||||
<manifest
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="org.ligi.passandroid">
|
|
||||||
|
|
||||||
<uses-sdk tools:overrideLibrary="com.jraska.falcon.spooncompat , com.jraska.falcon"/>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -15,7 +15,7 @@
|
||||||
"message":"",
|
"message":"",
|
||||||
"transfer":"barcode_QR",
|
"transfer":"barcode_QR",
|
||||||
"alternative": {
|
"alternative": {
|
||||||
"order":1,
|
"order":1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import org.ligi.trulesk.AppReplacingRunnerBase
|
||||||
|
|
||||||
|
class AppReplacingRunner : AppReplacingRunnerBase() {
|
||||||
|
|
||||||
|
override fun testAppClass() = TestApp::class.java
|
||||||
|
|
||||||
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import org.ligi.passandroid.reporting.SpooningFailureHandler;
|
|
||||||
import static android.support.test.espresso.Espresso.setFailureHandler;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseIntegration<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
|
|
||||||
|
|
||||||
public BaseIntegration(Class<T> activityClass) {
|
|
||||||
super(activityClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
setFailureHandler(new SpooningFailureHandler(getInstrumentation()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T getActivity() {
|
|
||||||
final T activity = super.getActivity();
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
|
|
||||||
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return activity;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package org.ligi.passandroid
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.support.test.InstrumentationRegistry
|
|
||||||
import android.support.test.espresso.Espresso.setFailureHandler
|
|
||||||
import android.view.WindowManager
|
|
||||||
import org.ligi.passandroid.reporting.SpooningFailureHandler
|
|
||||||
|
|
||||||
|
|
||||||
abstract class BaseUnitTest {
|
|
||||||
|
|
||||||
fun setUp(activity: Activity) {
|
|
||||||
setFailureHandler(SpooningFailureHandler(InstrumentationRegistry.getInstrumentation()))
|
|
||||||
|
|
||||||
activity.runOnUiThread { activity.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
58
android/src/androidTest/java/org/ligi/passandroid/TestApp.kt
Normal file
58
android/src/androidTest/java/org/ligi/passandroid/TestApp.kt
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import org.koin.core.module.Module
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.ligi.passandroid.injections.FixedPassListPassStore
|
||||||
|
import org.ligi.passandroid.model.PassStore
|
||||||
|
import org.ligi.passandroid.model.Settings
|
||||||
|
import org.ligi.passandroid.model.comparator.PassSortOrder
|
||||||
|
import org.ligi.passandroid.model.pass.BarCode
|
||||||
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class TestApp : App() {
|
||||||
|
|
||||||
|
override fun createKoin(): Module {
|
||||||
|
|
||||||
|
return module {
|
||||||
|
single { passStore as PassStore }
|
||||||
|
single { settings }
|
||||||
|
single { tracker }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val tracker = mock(Tracker::class.java)
|
||||||
|
val passStore = FixedPassListPassStore(emptyList())
|
||||||
|
val settings = mock(Settings::class.java).apply {
|
||||||
|
`when`(getSortOrder()).thenReturn(PassSortOrder.DATE_ASC)
|
||||||
|
`when`(getPassesDir()).thenReturn(File(""))
|
||||||
|
`when`(doTraceDroidEmailSend()).thenReturn(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun populatePassStoreWithSinglePass() {
|
||||||
|
|
||||||
|
val passList = ArrayList<Pass>()
|
||||||
|
val pass = PassImpl(UUID.randomUUID().toString())
|
||||||
|
pass.description = "description"
|
||||||
|
pass.barCode = BarCode(PassBarCodeFormat.AZTEC, "messageprobe")
|
||||||
|
passList.add(pass)
|
||||||
|
|
||||||
|
fixedPassListPassStore().setList(passList)
|
||||||
|
|
||||||
|
passStore.classifier.moveToTopic(pass, "test")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emptyPassStore() {
|
||||||
|
fixedPassListPassStore().setList(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fixedPassListPassStore() = passStore as FixedPassListPassStore
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
package org.ligi.passandroid
|
|
||||||
|
|
||||||
import dagger.Component
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = arrayOf(TestModule::class))
|
|
||||||
interface TestComponent : AppComponent {
|
|
||||||
|
|
||||||
fun inject(theFullscreenBarcodeActivity: TheFullscreenBarcodeActivity)
|
|
||||||
|
|
||||||
fun inject(thePassEditActivity: ThePassEditActivity)
|
|
||||||
|
|
||||||
fun inject(thePassViewActivity: ThePassViewActivity)
|
|
||||||
|
|
||||||
fun inject(thePastLocationsStore: ThePastLocationsStore)
|
|
||||||
|
|
||||||
fun inject(theBarCodeEditing: TheBarCodeEditing)
|
|
||||||
|
|
||||||
fun inject(thePassListSwiping: ThePassListSwiping)
|
|
||||||
|
|
||||||
fun inject(theFieldListEditFragment: TheFieldListEditFragment)
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
package org.ligi.passandroid
|
|
||||||
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.ligi.passandroid.injections.FixedPassListPassStore
|
|
||||||
import org.ligi.passandroid.model.PassStore
|
|
||||||
import org.ligi.passandroid.model.Settings
|
|
||||||
import org.ligi.passandroid.model.State
|
|
||||||
import org.ligi.passandroid.model.comparator.PassSortOrder
|
|
||||||
import org.ligi.passandroid.model.pass.BarCode
|
|
||||||
import org.ligi.passandroid.model.pass.Pass
|
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
|
||||||
import org.ligi.passandroid.model.pass.PassImpl
|
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import org.mockito.Mockito.mock
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Module
|
|
||||||
class TestModule {
|
|
||||||
|
|
||||||
private val passList: MutableList<Pass>
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
passList = ArrayList<Pass>()
|
|
||||||
val pass = PassImpl(UUID.randomUUID().toString())
|
|
||||||
pass.description = "description"
|
|
||||||
pass.barCode = BarCode(PassBarCodeFormat.AZTEC, "messageprobe")
|
|
||||||
passList.add(pass)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(passList: MutableList<Pass>) {
|
|
||||||
this.passList = passList
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun providePassStore(): PassStore {
|
|
||||||
val fixedPassListPassStore = FixedPassListPassStore(passList)
|
|
||||||
if (!passList.isEmpty()) {
|
|
||||||
fixedPassListPassStore.currentPass = passList[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (pass in passList) {
|
|
||||||
fixedPassListPassStore.classifier.moveToTopic(pass, "test")
|
|
||||||
}
|
|
||||||
return fixedPassListPassStore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideSettings(): Settings {
|
|
||||||
val mock = mock(Settings::class.java)
|
|
||||||
`when`(mock.sortOrder).thenReturn(PassSortOrder.DATE_ASC)
|
|
||||||
`when`(mock.passesDir).thenReturn(File(""))
|
|
||||||
`when`(mock.doTraceDroidEmailSend()).thenReturn(false)
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideBus(): EventBus {
|
|
||||||
return mock(EventBus::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideTracker(): Tracker {
|
|
||||||
return mock(Tracker::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideState(): State {
|
|
||||||
return State()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
|
import android.app.Instrumentation
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
|
import androidx.test.espresso.intent.Intents.intending
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasType
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import org.hamcrest.CoreMatchers.not
|
||||||
|
import org.hamcrest.Matchers.allOf
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.functions.DEFAULT_EVENT_LENGTH_IN_HOURS
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
import org.threeten.bp.ZonedDateTime
|
||||||
|
|
||||||
|
class TheAddToCalendar {
|
||||||
|
|
||||||
|
private val time = ZonedDateTime.now()
|
||||||
|
private val time2 = ZonedDateTime.now().plusHours(3)
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskIntentRule(PassListActivity::class.java, false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveCalendarStartDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
TestApp.passStore.currentPass!!.calendarTimespan = PassImpl.TimeSpan(time)
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time.plusHours(DEFAULT_EVENT_LENGTH_IN_HOURS).toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveCalendarEndDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
TestApp.passStore.currentPass!!.calendarTimespan = PassImpl.TimeSpan(to = time)
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.minusHours(DEFAULT_EVENT_LENGTH_IN_HOURS).toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveCalendarStartAndEndDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
TestApp.passStore.currentPass!!.calendarTimespan = PassImpl.TimeSpan(time, time2)
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time2.toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveExpirationDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
(TestApp.passStore.currentPass as PassImpl).validTimespans = listOf(PassImpl.TimeSpan(time))
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.expiration_date_to_calendar_warning_title)).check(matches(isDisplayed()))
|
||||||
|
onView(withText(android.R.string.ok)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time.plusHours(DEFAULT_EVENT_LENGTH_IN_HOURS).toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveExpirationEndDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
(TestApp.passStore.currentPass as PassImpl).validTimespans = listOf(PassImpl.TimeSpan(to = time))
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.expiration_date_to_calendar_warning_title)).check(matches(isDisplayed()))
|
||||||
|
onView(withText(android.R.string.ok)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.minusHours(DEFAULT_EVENT_LENGTH_IN_HOURS).toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIfWeOnlyHaveExpirationStartAndEndDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
|
||||||
|
(TestApp.passStore.currentPass as PassImpl).validTimespans = listOf(PassImpl.TimeSpan(time, time2))
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
intending(hasType("vnd.android.cursor.item/event")).respondWith(Instrumentation.ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.expiration_date_to_calendar_warning_title)).check(matches(isDisplayed()))
|
||||||
|
onView(withText(android.R.string.ok)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(
|
||||||
|
hasType("vnd.android.cursor.item/event"),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, time.toEpochSecond() * 1000),
|
||||||
|
hasExtra(CalendarContract.EXTRA_EVENT_END_TIME, time2.toEpochSecond() * 1000),
|
||||||
|
hasExtra("title", TestApp.passStore.currentPass!!.description)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testThereIsNoButtonWithNoDate() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
rule.launchActivity()
|
||||||
|
onView(withId(R.id.timeButton)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,71 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.ligi.passandroid.model.InputStreamWithSource;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
import org.ligi.passandroid.reader.AppleStylePassReader;
|
|
||||||
import org.ligi.passandroid.ui.UnzipPassController;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import static org.assertj.core.api.Fail.fail;
|
|
||||||
import static org.ligi.passandroid.ui.UnzipPassController.InputStreamUnzipControllerSpec;
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
public class TheAppleStyleBarcodeReaderBase {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
UnzipPassController.FailCallback failCallback;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
PassStore passStore;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getTestTargetPath(final Context context) {
|
|
||||||
return new File(context.getCacheDir() , "test_passes");
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadPassFromAsset(final String asset, final OnPassLoadCallback callback) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
|
||||||
final InputStream inputStream = instrumentation.getContext().getResources().getAssets().open(asset);
|
|
||||||
final InputStreamWithSource inputStreamWithSource = new InputStreamWithSource("none", inputStream);
|
|
||||||
|
|
||||||
final InputStreamUnzipControllerSpec spec = new InputStreamUnzipControllerSpec(inputStreamWithSource, instrumentation.getTargetContext(),passStore,
|
|
||||||
new UnzipPassController.SuccessCallback() {
|
|
||||||
@Override
|
|
||||||
public void call(String uuid) {
|
|
||||||
callback.onPassLoad(AppleStylePassReader.INSTANCE.read(new File(getTestTargetPath(instrumentation.getTargetContext()), uuid), "en",
|
|
||||||
instrumentation.getTargetContext()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
, failCallback
|
|
||||||
);
|
|
||||||
|
|
||||||
spec.setOverwrite(true);
|
|
||||||
spec.setTargetPath(getTestTargetPath(spec.getContext()));
|
|
||||||
UnzipPassController.INSTANCE.processInputStream(spec);
|
|
||||||
|
|
||||||
verify(failCallback, never()).fail(any(String.class));
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("should be able to load file ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnPassLoadCallback {
|
|
||||||
void onPassLoad(Pass pass);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,16 @@
|
||||||
package org.ligi.passandroid
|
package org.ligi.passandroid
|
||||||
|
|
||||||
import android.support.test.espresso.Espresso.closeSoftKeyboard
|
import android.Manifest
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import android.support.test.espresso.action.ViewActions.*
|
import androidx.test.espresso.Espresso.closeSoftKeyboard
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.doesNotExist
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.action.ViewActions.*
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||||
import android.support.test.filters.SdkSuppress
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.rule.ActivityTestRule
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import android.support.test.runner.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.squareup.spoon.Spoon
|
import androidx.test.filters.SdkSuppress
|
||||||
|
import com.linkedin.android.testbutler.TestButler
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -18,33 +19,29 @@ import org.ligi.passandroid.model.PassStore
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
import org.ligi.passandroid.model.pass.PassImpl
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
import org.ligi.passandroid.ui.PassEditActivity
|
import org.ligi.passandroid.ui.PassEditActivity
|
||||||
import javax.inject.Inject
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class TheBarCodeEditing : BaseUnitTest() {
|
class TheBarCodeEditing {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val rule: ActivityTestRule<PassEditActivity> = ActivityTestRule(PassEditActivity::class.java, true, false)
|
val rule = TruleskActivityRule(PassEditActivity::class.java, false)
|
||||||
|
|
||||||
@Inject
|
val passStore: PassStore = TestApp.passStore
|
||||||
lateinit var passStore: PassStore
|
|
||||||
|
|
||||||
lateinit var currentPass: PassImpl
|
private lateinit var currentPass: PassImpl
|
||||||
|
|
||||||
private val passEditActivity by lazy { rule.launchActivity(null) }
|
private fun start(setupPass: (pass: PassImpl) -> Unit = {}) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
fun start(setupPass: (pass: PassImpl) -> Unit) {
|
|
||||||
|
|
||||||
val build = DaggerTestComponent.create()
|
|
||||||
build.inject(this)
|
|
||||||
App.setComponent(build)
|
|
||||||
|
|
||||||
currentPass = passStore.currentPass as PassImpl
|
currentPass = passStore.currentPass as PassImpl
|
||||||
|
|
||||||
setupPass(currentPass)
|
setupPass(currentPass)
|
||||||
|
|
||||||
setUp(passEditActivity)
|
TestButler.grantPermission(ApplicationProvider.getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
TestButler.grantPermission(ApplicationProvider.getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
|
||||||
|
rule.launchActivity(null)
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +52,7 @@ class TheBarCodeEditing : BaseUnitTest() {
|
||||||
it.barCode = null
|
it.barCode = null
|
||||||
}
|
}
|
||||||
|
|
||||||
Spoon.screenshot(passEditActivity!!, "no_barcode")
|
rule.screenShot("no_barcode")
|
||||||
|
|
||||||
onView(withId(R.id.add_barcode_button)).perform(scrollTo())
|
onView(withId(R.id.add_barcode_button)).perform(scrollTo())
|
||||||
onView(withId(R.id.add_barcode_button)).check(matches(isDisplayed()))
|
onView(withId(R.id.add_barcode_button)).check(matches(isDisplayed()))
|
||||||
|
@ -81,30 +78,31 @@ class TheBarCodeEditing : BaseUnitTest() {
|
||||||
@SdkSuppress(minSdkVersion = 14)
|
@SdkSuppress(minSdkVersion = 14)
|
||||||
@Test
|
@Test
|
||||||
fun testCanSetToAllBarcodeTypes() {
|
fun testCanSetToAllBarcodeTypes() {
|
||||||
start {}
|
start()
|
||||||
for (passBarCodeFormat in PassBarCodeFormat.values()) {
|
for (passBarCodeFormat in PassBarCodeFormat.values()) {
|
||||||
onView(withId(R.id.barcode_img)).perform(scrollTo(), click())
|
onView(withId(R.id.barcode_img)).perform(scrollTo(), click())
|
||||||
|
|
||||||
onView(withText(passBarCodeFormat.name)).perform(scrollTo(), click())
|
onView(withText(passBarCodeFormat.name)).perform(scrollTo(), click())
|
||||||
|
|
||||||
|
onView(withId(R.id.randomButton)).perform(click())
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
|
|
||||||
onView(withText(android.R.string.ok)).perform(click())
|
onView(withText(android.R.string.ok)).perform(click())
|
||||||
|
|
||||||
assertThat(currentPass.barCode!!.format).isEqualTo(passBarCodeFormat)
|
assertThat(currentPass.barCode!!.format).isEqualTo(passBarCodeFormat)
|
||||||
Spoon.screenshot(passEditActivity!!, "edit_set_" + passBarCodeFormat.name)
|
rule.screenShot("edit_set_" + passBarCodeFormat.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCanSetMessage() {
|
fun testCanSetMessage() {
|
||||||
start {}
|
start()
|
||||||
|
|
||||||
onView(withId(R.id.barcode_img)).perform(click())
|
onView(withId(R.id.barcode_img)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.messageInput)).perform(clearText())
|
onView(withId(R.id.messageInput)).perform(clearText())
|
||||||
onView(withId(R.id.messageInput)).perform(typeText("msg foo txt ;-)"))
|
onView(withId(R.id.messageInput)).perform(replaceText("msg foo txt ;-)"))
|
||||||
|
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
|
|
||||||
|
@ -113,18 +111,18 @@ class TheBarCodeEditing : BaseUnitTest() {
|
||||||
onView(withText(R.string.edit_barcode_dialog_title)).check(doesNotExist())
|
onView(withText(R.string.edit_barcode_dialog_title)).check(doesNotExist())
|
||||||
|
|
||||||
assertThat(passStore.currentPass!!.barCode!!.message).isEqualTo("msg foo txt ;-)")
|
assertThat(passStore.currentPass!!.barCode!!.message).isEqualTo("msg foo txt ;-)")
|
||||||
Spoon.screenshot(passEditActivity!!, "edit_set_msg")
|
rule.screenShot("edit_set_msg")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCanSetAltMessage() {
|
fun testCanSetAltMessage() {
|
||||||
start {}
|
start()
|
||||||
|
|
||||||
onView(withId(R.id.barcode_img)).perform(click())
|
onView(withId(R.id.barcode_img)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.alternativeMessageInput)).perform(clearText())
|
onView(withId(R.id.alternativeMessageInput)).perform(clearText())
|
||||||
onView(withId(R.id.alternativeMessageInput)).perform(typeText("alt bar txt ;-)"))
|
onView(withId(R.id.alternativeMessageInput)).perform(replaceText("alt bar txt ;-)"))
|
||||||
|
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
|
|
||||||
|
@ -133,12 +131,12 @@ class TheBarCodeEditing : BaseUnitTest() {
|
||||||
onView(withText(R.string.edit_barcode_dialog_title)).check(doesNotExist())
|
onView(withText(R.string.edit_barcode_dialog_title)).check(doesNotExist())
|
||||||
|
|
||||||
assertThat(passStore.currentPass!!.barCode!!.alternativeText).isEqualTo("alt bar txt ;-)")
|
assertThat(passStore.currentPass!!.barCode!!.alternativeText).isEqualTo("alt bar txt ;-)")
|
||||||
Spoon.screenshot(passEditActivity!!, "edit_set_altmsg")
|
rule.screenShot("edit_set_altmsg")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testThatRandomChangesMessage() {
|
fun testThatRandomChangesMessage() {
|
||||||
start {}
|
start()
|
||||||
|
|
||||||
onView(withId(R.id.barcode_img)).perform(click())
|
onView(withId(R.id.barcode_img)).perform(click())
|
||||||
|
|
||||||
|
@ -152,5 +150,4 @@ class TheBarCodeEditing : BaseUnitTest() {
|
||||||
assertThat(oldMessage).isNotEqualTo(passStore.currentPass!!.barCode!!.message)
|
assertThat(oldMessage).isNotEqualTo(passStore.currentPass!!.barCode!!.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import com.google.zxing.common.BitMatrix;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.ligi.passandroid.helper.BarcodeHelper;
|
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Fail.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class TheBarcodeHelper {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testQRBitMatrixHasCorrectSize() throws Exception {
|
|
||||||
testBitMatrixSizeIsSane(PassBarCodeFormat.QR_CODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testQRBitmapHasCorrectSize() throws Exception {
|
|
||||||
testBitmapSizeIsSane(PassBarCodeFormat.QR_CODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPDF417BitmapHasCorrectSize() {
|
|
||||||
testBitmapSizeIsSane(PassBarCodeFormat.PDF_417);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPDF417BitMatrixHasCorrectSize() {
|
|
||||||
testBitMatrixSizeIsSane(PassBarCodeFormat.PDF_417);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAZTECBitmapHasCorrectSize() {
|
|
||||||
testBitmapSizeIsSane(PassBarCodeFormat.AZTEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAZTECBitMatrixHasCorrectSize() {
|
|
||||||
testBitMatrixSizeIsSane(PassBarCodeFormat.AZTEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBitMatrixSizeIsSane(final PassBarCodeFormat format) {
|
|
||||||
try {
|
|
||||||
BitMatrix tested = BarcodeHelper.getBitMatrix("foo-data", format);
|
|
||||||
|
|
||||||
assertThat(tested.getWidth()).isGreaterThan(3);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("could not create barcode", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBitmapSizeIsSane(final PassBarCodeFormat format) {
|
|
||||||
try {
|
|
||||||
Bitmap tested2 = BarcodeHelper.generateBarCodeBitmap("foo-data", format);
|
|
||||||
|
|
||||||
assert tested2 != null;
|
|
||||||
assertThat(tested2.getWidth()).isGreaterThan(3);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("could not create barcode" ,e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Fail.fail
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.functions.generateBarCodeBitmap
|
||||||
|
import org.ligi.passandroid.functions.getBitMatrix
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
|
|
||||||
|
class TheBarcodeHelper {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testQRBitMatrixHasCorrectSize() {
|
||||||
|
testBitMatrixSizeIsSane(PassBarCodeFormat.QR_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testQRBitmapHasCorrectSize() {
|
||||||
|
testBitmapSizeIsSane(PassBarCodeFormat.QR_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPDF417BitmapHasCorrectSize() {
|
||||||
|
testBitmapSizeIsSane(PassBarCodeFormat.PDF_417)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPDF417BitMatrixHasCorrectSize() {
|
||||||
|
testBitMatrixSizeIsSane(PassBarCodeFormat.PDF_417)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAZTECBitmapHasCorrectSize() {
|
||||||
|
testBitmapSizeIsSane(PassBarCodeFormat.AZTEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAZTECBitMatrixHasCorrectSize() {
|
||||||
|
testBitMatrixSizeIsSane(PassBarCodeFormat.AZTEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testBitMatrixSizeIsSane(format: PassBarCodeFormat) {
|
||||||
|
try {
|
||||||
|
val tested = getBitMatrix("foo-data", format)
|
||||||
|
|
||||||
|
assertThat(tested.width).isGreaterThan(3)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
fail("could not create barcode", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testBitmapSizeIsSane(format: PassBarCodeFormat) {
|
||||||
|
try {
|
||||||
|
val tested2 = generateBarCodeBitmap("foo-data", format)!!
|
||||||
|
|
||||||
|
assertThat(tested2.width).isGreaterThan(3)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
fail("could not create barcode", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.ligi.passandroid.helper.CategoryHelper;
|
|
||||||
import org.ligi.passandroid.model.PassDefinitions;
|
|
||||||
import org.ligi.passandroid.model.pass.PassType;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
public class TheCategoryHelper {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAllCategoriesAreTranslated() {
|
|
||||||
|
|
||||||
final Set<Integer> probe = new HashSet<>();
|
|
||||||
|
|
||||||
for (PassType type : PassDefinitions.INSTANCE.getTYPES().keySet()) {
|
|
||||||
probe.add(CategoryHelper.INSTANCE.getHumanCategoryString(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(probe.size()).isEqualTo(PassDefinitions.INSTANCE.getTYPES().keySet().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.functions.getHumanCategoryString
|
||||||
|
import org.ligi.passandroid.model.PassDefinitions
|
||||||
|
|
||||||
|
class TheCategoryHelper {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAllCategoriesAreTranslated() {
|
||||||
|
|
||||||
|
val allTranslationSet = PassDefinitions.TYPE_TO_NAME.keys
|
||||||
|
.map(::getHumanCategoryString)
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
assertThat(allTranslationSet.size).isEqualTo(PassDefinitions.TYPE_TO_NAME.keys.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import org.hamcrest.CoreMatchers.containsString
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.pass.PassField
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.threeten.bp.ZoneId
|
||||||
|
import org.threeten.bp.ZonedDateTime
|
||||||
|
|
||||||
|
class TheCondensedPassViewMode {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskActivityRule(PassListActivity::class.java, false) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
val currentPass = TestApp.passStore.currentPass as PassImpl
|
||||||
|
currentPass.calendarTimespan = PassImpl.TimeSpan(ZonedDateTime.of(2016, 11, 23, 20, 42, 42, 5, ZoneId.systemDefault()))
|
||||||
|
currentPass.fields = mutableListOf(PassField("textprobe", "bar", "yo", false))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDateShowsForCondensedOff() {
|
||||||
|
`when`(TestApp.settings.isCondensedModeEnabled()).thenReturn(false)
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.date)).check(matches(withText(containsString("23"))))
|
||||||
|
onView(withId(R.id.timeButton)).check(matches(withText(R.string.pass_to_calendar)))
|
||||||
|
|
||||||
|
rule.screenShot("condensed_off")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFieldShowsForCondensedOn() {
|
||||||
|
|
||||||
|
`when`(TestApp.settings.isCondensedModeEnabled()).thenReturn(true)
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.date)).check(matches(withText(containsString("bar"))))
|
||||||
|
onView(withId(R.id.timeButton)).check(matches(withText(containsString("23"))))
|
||||||
|
|
||||||
|
rule.screenShot("condensed_on")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,42 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.support.test.filters.MediumTest;
|
|
||||||
import com.squareup.spoon.Spoon;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
import org.ligi.passandroid.ui.PassListActivity;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.ligi.passandroid.steps.HelpSteps.checkThatHelpIsThere;
|
|
||||||
|
|
||||||
public class TheEmptyPassList extends BaseIntegration<PassListActivity> {
|
|
||||||
|
|
||||||
public TheEmptyPassList() {
|
|
||||||
super(PassListActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
App.setComponent(DaggerTestComponent.builder().testModule(new TestModule(new ArrayList<Pass>())).build());
|
|
||||||
getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testEmptyViewIsThereWhenThereAreNoPasses() {
|
|
||||||
Spoon.screenshot(getActivity(), "empty_view");
|
|
||||||
onView(withId(R.id.emptyView)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testHelpGoesToHelp() {
|
|
||||||
onView(withId(R.id.menu_help)).perform(click());
|
|
||||||
|
|
||||||
checkThatHelpIsThere();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.R.id.emptyView
|
||||||
|
import org.ligi.passandroid.functions.checkThatHelpIsThere
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
|
||||||
|
|
||||||
|
class TheEmptyPassList {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskIntentRule(PassListActivity::class.java) {
|
||||||
|
TestApp.emptyPassStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmptyViewIsThereWhenThereAreNoPasses() {
|
||||||
|
rule.screenShot("empty_view")
|
||||||
|
onView(withId(emptyView)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHelpGoesToHelp() {
|
||||||
|
onView(withId(R.id.menu_help)).perform(click())
|
||||||
|
|
||||||
|
checkThatHelpIsThere()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,54 +1,34 @@
|
||||||
package org.ligi.passandroid
|
package org.ligi.passandroid
|
||||||
|
|
||||||
import android.app.Activity
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.test.espresso.action.ViewActions.replaceText
|
||||||
import android.support.test.espresso.action.ViewActions.replaceText
|
import androidx.test.espresso.action.ViewActions.scrollTo
|
||||||
import android.support.test.espresso.action.ViewActions.scrollTo
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.*
|
|
||||||
import android.support.test.rule.ActivityTestRule
|
|
||||||
import android.support.test.runner.AndroidJUnit4
|
|
||||||
import com.squareup.spoon.Spoon
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.ligi.passandroid.model.PassStore
|
|
||||||
import org.ligi.passandroid.model.pass.PassField
|
import org.ligi.passandroid.model.pass.PassField
|
||||||
import org.ligi.passandroid.model.pass.PassImpl
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
import org.ligi.passandroid.ui.PassEditActivity
|
import org.ligi.passandroid.ui.PassEditActivity
|
||||||
import javax.inject.Inject
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
class TheFieldListEditFragment {
|
||||||
class TheFieldListEditFragment : BaseUnitTest(){
|
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val rule: ActivityTestRule<PassEditActivity> = ActivityTestRule(PassEditActivity::class.java, true, false)
|
val rule = TruleskIntentRule(PassEditActivity::class.java) {
|
||||||
|
TestApp.passStore.currentPass = PassImpl(UUID.randomUUID().toString()).apply {
|
||||||
@Inject
|
fields = arrayListOf(field)
|
||||||
lateinit var passStore: PassStore
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val field: PassField = PassField(null, "labelfieldcontent", "valuefieldcontent", false)
|
private val field: PassField = PassField(null, "labelfieldcontent", "valuefieldcontent", false)
|
||||||
|
|
||||||
fun start(): Activity {
|
|
||||||
val build = DaggerTestComponent.create()
|
|
||||||
build.inject(this)
|
|
||||||
App.setComponent(build)
|
|
||||||
|
|
||||||
val currentPass = passStore.currentPass as PassImpl
|
|
||||||
|
|
||||||
currentPass.fields = arrayListOf(field)
|
|
||||||
|
|
||||||
val activity = rule.launchActivity(null)
|
|
||||||
|
|
||||||
setUp(activity)
|
|
||||||
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFieldDetailsArePreFilled() {
|
fun testFieldDetailsArePreFilled() {
|
||||||
Spoon.screenshot(start(), "one_field")
|
|
||||||
|
rule.screenShot("one_field")
|
||||||
|
|
||||||
onView(withId(R.id.label_field_edit)).perform(scrollTo())
|
onView(withId(R.id.label_field_edit)).perform(scrollTo())
|
||||||
onView(withId(R.id.label_field_edit)).check(matches(isDisplayed()))
|
onView(withId(R.id.label_field_edit)).check(matches(isDisplayed()))
|
||||||
|
@ -63,8 +43,6 @@ class TheFieldListEditFragment : BaseUnitTest(){
|
||||||
@Test
|
@Test
|
||||||
fun testThatChangingLabelWorks() {
|
fun testThatChangingLabelWorks() {
|
||||||
|
|
||||||
start()
|
|
||||||
|
|
||||||
onView(withId(R.id.label_field_edit)).perform(scrollTo())
|
onView(withId(R.id.label_field_edit)).perform(scrollTo())
|
||||||
onView(withId(R.id.label_field_edit)).perform(replaceText("newlabel"))
|
onView(withId(R.id.label_field_edit)).perform(replaceText("newlabel"))
|
||||||
assertThat(field.label).isEqualTo("newlabel")
|
assertThat(field.label).isEqualTo("newlabel")
|
||||||
|
@ -73,8 +51,6 @@ class TheFieldListEditFragment : BaseUnitTest(){
|
||||||
@Test
|
@Test
|
||||||
fun testThatChangingValueWorks() {
|
fun testThatChangingValueWorks() {
|
||||||
|
|
||||||
start()
|
|
||||||
|
|
||||||
onView(withId(R.id.value_field_edit)).perform(scrollTo())
|
onView(withId(R.id.value_field_edit)).perform(scrollTo())
|
||||||
onView(withId(R.id.value_field_edit)).perform(replaceText("newvalue"))
|
onView(withId(R.id.value_field_edit)).perform(replaceText("newvalue"))
|
||||||
assertThat(field.value).isEqualTo("newvalue")
|
assertThat(field.value).isEqualTo("newvalue")
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.support.test.filters.MediumTest;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import com.squareup.spoon.Spoon;
|
|
||||||
import java.util.UUID;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.ligi.passandroid.helper.BarcodeDecoder;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.pass.BarCode;
|
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat;
|
|
||||||
import org.ligi.passandroid.model.pass.PassImpl;
|
|
||||||
import org.ligi.passandroid.ui.FullscreenBarcodeActivity;
|
|
||||||
import org.ligi.tracedroid.TraceDroid;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
public class TheFullscreenBarcodeActivity extends BaseIntegration<FullscreenBarcodeActivity> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
PassStore passStore;
|
|
||||||
|
|
||||||
public static final String BARCODE_MESSAGE = "2323";
|
|
||||||
|
|
||||||
public TheFullscreenBarcodeActivity() {
|
|
||||||
super(FullscreenBarcodeActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
TestComponent component = DaggerTestComponent.create();
|
|
||||||
|
|
||||||
component.inject(this);
|
|
||||||
|
|
||||||
App.setComponent(component);
|
|
||||||
|
|
||||||
TraceDroid.deleteStacktraceFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testPDF417BarcodeIsShown() {
|
|
||||||
testWithBarcodeFormat(PassBarCodeFormat.PDF_417);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "pdf417_barcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testAztecBarcodeIsShown() {
|
|
||||||
testWithBarcodeFormat(PassBarCodeFormat.AZTEC);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "aztec_barcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testQRCodeIsShown() {
|
|
||||||
testWithBarcodeFormat(PassBarCodeFormat.QR_CODE);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "qr_barcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testCode128CodeIsShown() {
|
|
||||||
testWithBarcodeFormat(PassBarCodeFormat.CODE_128);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "code128_barcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testCode39CodeIsShown() {
|
|
||||||
testWithBarcodeFormat(PassBarCodeFormat.CODE_39);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "code39_barcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void testWithBarcodeFormat(final PassBarCodeFormat format) {
|
|
||||||
final PassImpl pass = new PassImpl(UUID.randomUUID().toString());
|
|
||||||
pass.setBarCode(new BarCode(format, BARCODE_MESSAGE));
|
|
||||||
|
|
||||||
passStore.setCurrentPass(pass);
|
|
||||||
getActivity();
|
|
||||||
onView(withId(R.id.fullscreen_barcode)).check(matches(isDisplayed()));
|
|
||||||
|
|
||||||
final ImageView viewById = ButterKnife.findById(getActivity(), R.id.fullscreen_barcode);
|
|
||||||
BitmapDrawable bitmapDrawable = (BitmapDrawable) viewById.getDrawable();
|
|
||||||
final Bitmap bitmap = bitmapDrawable.getBitmap();
|
|
||||||
|
|
||||||
final Bitmap bitmapToTest;
|
|
||||||
if (format == PassBarCodeFormat.AZTEC) {
|
|
||||||
// not sure why - but for the decoder to pick up AZTEC it must have moar pixelz - smells like a zxing bug
|
|
||||||
bitmapToTest = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() * 2, bitmap.getHeight() * 2, false);
|
|
||||||
} else {
|
|
||||||
bitmapToTest = bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(BarcodeDecoder.INSTANCE.decodeBitmap(bitmapToTest)).isEqualTo(BARCODE_MESSAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.functions.decodeBarCode
|
||||||
|
import org.ligi.passandroid.model.pass.BarCode
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat.*
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.ui.FullscreenBarcodeActivity
|
||||||
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
private const val BARCODE_MESSAGE = "2323"
|
||||||
|
|
||||||
|
class TheFullscreenBarcodeActivity {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskIntentRule(FullscreenBarcodeActivity::class.java, false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPDF417BarcodeIsShown() {
|
||||||
|
testWithBarcodeFormat(PDF_417)
|
||||||
|
|
||||||
|
rule.screenShot("pdf417_barcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAztecBarcodeIsShown() {
|
||||||
|
testWithBarcodeFormat(AZTEC)
|
||||||
|
|
||||||
|
rule.screenShot("aztec_barcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testQRCodeIsShown() {
|
||||||
|
testWithBarcodeFormat(QR_CODE)
|
||||||
|
|
||||||
|
rule.screenShot("qr_barcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCode128CodeIsShown() {
|
||||||
|
testWithBarcodeFormat(CODE_128)
|
||||||
|
|
||||||
|
rule.screenShot("code128_barcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCode39CodeIsShown() {
|
||||||
|
testWithBarcodeFormat(CODE_39)
|
||||||
|
|
||||||
|
rule.screenShot("code39_barcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun testWithBarcodeFormat(format: PassBarCodeFormat) {
|
||||||
|
val pass = PassImpl(UUID.randomUUID().toString())
|
||||||
|
pass.barCode = BarCode(format, BARCODE_MESSAGE)
|
||||||
|
|
||||||
|
TestApp.passStore.currentPass = pass
|
||||||
|
|
||||||
|
rule.launchActivity(null)
|
||||||
|
onView(withId(R.id.fullscreen_barcode)).check(matches(isDisplayed()))
|
||||||
|
|
||||||
|
val viewById = rule.activity.findViewById(R.id.fullscreen_barcode) as ImageView
|
||||||
|
val bitmapDrawable = viewById.drawable as BitmapDrawable
|
||||||
|
val bitmap = bitmapDrawable.bitmap
|
||||||
|
|
||||||
|
val bitmapToTest: Bitmap
|
||||||
|
bitmapToTest = if (format === PassBarCodeFormat.AZTEC) {
|
||||||
|
// not sure why - but for the decoder to pick up AZTEC it must have moar pixelz - smells like a zxing bug
|
||||||
|
Bitmap.createScaledBitmap(bitmap, bitmap.width * 2, bitmap.height * 2, false)
|
||||||
|
} else {
|
||||||
|
bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(bitmapToTest.decodeBarCode()).isEqualTo(BARCODE_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,32 +1,32 @@
|
||||||
package org.ligi.passandroid
|
package org.ligi.passandroid
|
||||||
|
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import android.support.test.rule.ActivityTestRule
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import junit.framework.Assert
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.ligi.passandroid.helper.ScreenshotTaker
|
import org.ligi.passandroid.functions.checkThatHelpIsThere
|
||||||
import org.ligi.passandroid.steps.HelpSteps.checkThatHelpIsThere
|
|
||||||
import org.ligi.passandroid.ui.HelpActivity
|
import org.ligi.passandroid.ui.HelpActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
|
||||||
class TheHelpActivity {
|
class TheHelpActivity {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val rule: ActivityTestRule<HelpActivity> = ActivityTestRule(HelpActivity::class.java)
|
val rule = TruleskActivityRule(HelpActivity::class.java)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHelpIsThere() {
|
fun testHelpIsThere() {
|
||||||
checkThatHelpIsThere()
|
checkThatHelpIsThere()
|
||||||
ScreenshotTaker.takeScreenshot(rule.activity, "help")
|
rule.screenShot("help")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_that_help_finishes_on_home() {
|
fun test_that_help_finishes_on_home() {
|
||||||
onView(withContentDescription(R.string.abc_action_bar_up_description)).perform(click())
|
onView(withContentDescription(R.string.abc_action_bar_up_description)).perform(click())
|
||||||
Assert.assertTrue(rule.activity.isFinishing)
|
|
||||||
|
assertThat(rule.activity.isFinishing).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
|
import android.app.Instrumentation.ActivityResult
|
||||||
|
import android.content.Intent.ACTION_SEND
|
||||||
|
import android.content.Intent.ACTION_VIEW
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.Espresso.pressBack
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.contrib.DrawerActions.open
|
||||||
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
|
import androidx.test.espresso.intent.Intents.intending
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.*
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import org.hamcrest.Matchers.allOf
|
||||||
|
import org.hamcrest.Matchers.not
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.R.string.*
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.passandroid.ui.PreferenceActivity
|
||||||
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
|
||||||
|
@TargetApi(14)
|
||||||
|
class TheNavigationDrawer {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskIntentRule(PassListActivity::class.java)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNavigationDrawerIsUsuallyNotShown() {
|
||||||
|
onView(withId(R.id.navigationView)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testThatNavigationDrawerOpens() {
|
||||||
|
onView(withId(R.id.drawer_layout)).perform(open())
|
||||||
|
onView(withId(R.id.navigationView)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testThatNavigationDrawerClosesOnBackPress() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
|
||||||
|
pressBack()
|
||||||
|
|
||||||
|
onView(withId(R.id.navigationView)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBetatestClick() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
rule.screenShot("open_drawer")
|
||||||
|
|
||||||
|
intending(hasAction(ACTION_VIEW)).respondWith(ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withText(nav_betatest_opt_in_out)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(hasAction(ACTION_VIEW), hasData("https://play.google.com/apps/testing/org.ligi.passandroid")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGitHubClick() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
rule.screenShot("open_drawer")
|
||||||
|
|
||||||
|
intending(hasAction(ACTION_VIEW)).respondWith(ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withText(nav_github)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(hasAction(ACTION_VIEW), hasData("https://github.com/ligi/PassAndroid")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testImproveTranslationsClick() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
rule.screenShot("open_drawer")
|
||||||
|
|
||||||
|
intending(hasAction(ACTION_VIEW)).respondWith(ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withText(nav_improve_translation)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(hasAction(ACTION_VIEW), hasData("https://transifex.com/projects/p/passandroid")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testShareClick() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
rule.screenShot("open_drawer")
|
||||||
|
|
||||||
|
intending(hasAction(ACTION_SEND)).respondWith(ActivityResult(RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withText(nav_share)).perform(click())
|
||||||
|
|
||||||
|
intended(allOf(hasAction(ACTION_SEND), hasType("text/plain")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSettings() {
|
||||||
|
testThatNavigationDrawerOpens()
|
||||||
|
rule.screenShot("open_drawer")
|
||||||
|
|
||||||
|
onView(withText(nav_settings)).perform(click())
|
||||||
|
|
||||||
|
intended(hasComponent(PreferenceActivity::class.java.name))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,85 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.support.test.filters.MediumTest;
|
|
||||||
import com.squareup.spoon.Spoon;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.pass.PassType;
|
|
||||||
import org.ligi.passandroid.ui.PassEditActivity;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.clearText;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.typeText;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
@TargetApi(14)
|
|
||||||
public class ThePassEditActivity extends BaseIntegration<PassEditActivity> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
PassStore passStore;
|
|
||||||
|
|
||||||
public ThePassEditActivity() {
|
|
||||||
super(PassEditActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
final TestComponent build = DaggerTestComponent.create();
|
|
||||||
build.inject(this);
|
|
||||||
App.setComponent(build);
|
|
||||||
|
|
||||||
getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testSetToEventWorks() {
|
|
||||||
|
|
||||||
onView(withId(R.id.categoryView)).perform(click());
|
|
||||||
|
|
||||||
onView(withText(R.string.select_category_dialog_title)).perform(click());
|
|
||||||
onView(withText(R.string.category_event)).perform(click());
|
|
||||||
assertThat(passStore.getCurrentPass().getType()).isEqualTo(PassType.EVENT);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "edit_set_event");
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testSetToCouponWorks() {
|
|
||||||
onView(withId(R.id.categoryView)).perform(click());
|
|
||||||
|
|
||||||
onView(withText(R.string.select_category_dialog_title)).perform(click());
|
|
||||||
onView(withText(R.string.category_coupon)).perform(click());
|
|
||||||
assertThat(passStore.getCurrentPass().getType()).isEqualTo(PassType.COUPON);
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "edit_set_coupon");
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testSetDescriptionWorks() {
|
|
||||||
|
|
||||||
onView(withId(R.id.title)).perform(clearText(),typeText("test description"));
|
|
||||||
assertThat(passStore.getCurrentPass().getDescription()).isEqualTo("test description");
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "edit_set_description");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testColorWheelIsThere() {
|
|
||||||
|
|
||||||
onView(withId(R.id.categoryView)).perform(click());
|
|
||||||
onView(withText(R.string.change_color_dialog_title)).perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.colorPicker)).check(matches(isDisplayed()));
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "edit_set_color");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Instrumentation
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.*
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
|
import androidx.test.espresso.intent.Intents.intending
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.linkedin.android.testbutler.TestButler
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.pass.PassType.COUPON
|
||||||
|
import org.ligi.passandroid.model.pass.PassType.EVENT
|
||||||
|
import org.ligi.passandroid.ui.PassEditActivity
|
||||||
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
|
||||||
|
@TargetApi(14)
|
||||||
|
class ThePassEditActivity {
|
||||||
|
|
||||||
|
val passStore = TestApp.passStore
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskIntentRule(PassEditActivity::class.java) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
TestButler.grantPermission(InstrumentationRegistry.getInstrumentation().targetContext, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
TestButler.grantPermission(InstrumentationRegistry.getInstrumentation().targetContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetToEventWorks() {
|
||||||
|
onView(withId(R.id.categoryView)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.select_category_dialog_title)).perform(click())
|
||||||
|
onView(withText(R.string.category_event)).perform(click())
|
||||||
|
assertThat(passStore.currentPass!!.type).isEqualTo(EVENT)
|
||||||
|
|
||||||
|
rule.screenShot("edit_set_event")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetToCouponWorks() {
|
||||||
|
onView(withId(R.id.categoryView)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.select_category_dialog_title)).perform(click())
|
||||||
|
onView(withText(R.string.category_coupon)).perform(click())
|
||||||
|
assertThat(passStore.currentPass!!.type).isEqualTo(COUPON)
|
||||||
|
|
||||||
|
rule.screenShot("edit_set_coupon")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetDescriptionWorks() {
|
||||||
|
|
||||||
|
onView(withId(R.id.passTitle)).perform(clearText(), replaceText("test description"))
|
||||||
|
assertThat(passStore.currentPass!!.description).isEqualTo("test description")
|
||||||
|
|
||||||
|
rule.screenShot("edit_set_description")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testColorWheelIsThere() {
|
||||||
|
|
||||||
|
onView(withId(R.id.categoryView)).perform(click())
|
||||||
|
onView(withText(R.string.change_color_dialog_title)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.colorPicker)).check(matches(isDisplayed()))
|
||||||
|
|
||||||
|
rule.screenShot("edit_set_color")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddAbortFooterImagePick() {
|
||||||
|
intending(hasAction(Intent.ACTION_CHOOSER)).respondWith(Instrumentation.ActivityResult(Activity.RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.add_footer)).perform(scrollTo(), click())
|
||||||
|
|
||||||
|
intended(hasAction(Intent.ACTION_CHOOSER))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddAbortStripImagePick() {
|
||||||
|
intending(hasAction(Intent.ACTION_CHOOSER)).respondWith(Instrumentation.ActivityResult(Activity.RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.add_strip)).perform(scrollTo(), click())
|
||||||
|
|
||||||
|
intended(hasAction(Intent.ACTION_CHOOSER))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddAbortLogoImagePick() {
|
||||||
|
|
||||||
|
intending(hasAction(Intent.ACTION_CHOOSER)).respondWith(Instrumentation.ActivityResult(Activity.RESULT_CANCELED, null))
|
||||||
|
|
||||||
|
onView(withId(R.id.add_logo)).perform(scrollTo(), click())
|
||||||
|
|
||||||
|
intended(hasAction(Intent.ACTION_CHOOSER))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.support.test.filters.MediumTest;
|
|
||||||
import com.squareup.spoon.Spoon;
|
|
||||||
import org.ligi.passandroid.ui.PassListActivity;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.contrib.DrawerActions.open;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
|
||||||
import static org.ligi.passandroid.steps.HelpSteps.checkThatHelpIsThere;
|
|
||||||
|
|
||||||
@TargetApi(14)
|
|
||||||
public class ThePassListActivity extends BaseIntegration<PassListActivity> {
|
|
||||||
|
|
||||||
|
|
||||||
public ThePassListActivity() {
|
|
||||||
super(PassListActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
App.setComponent(DaggerTestComponent.builder().build());
|
|
||||||
getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testListIsThere() {
|
|
||||||
|
|
||||||
onView(withId(R.id.pass_recyclerview)).check(matches(isDisplayed()));
|
|
||||||
Spoon.screenshot(getActivity(), "list");
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testHelpMenuBringsUsToHelp() {
|
|
||||||
onView(withId(R.id.menu_help)).perform(click());
|
|
||||||
|
|
||||||
checkThatHelpIsThere();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testNavigationDrawerIsUsuallyNotShown() {
|
|
||||||
onView(withId(R.id.navigationView)).check(matches(not(isDisplayed())));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testThatNavigationDrawerOpens() {
|
|
||||||
|
|
||||||
onView(withId(R.id.drawer_layout)).perform(open());
|
|
||||||
onView(withId(R.id.navigationView)).check(matches(isDisplayed()));
|
|
||||||
|
|
||||||
Spoon.screenshot(getActivity(), "open_drawer");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.Espresso.pressBack
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.R.id.pass_recyclerview
|
||||||
|
import org.ligi.passandroid.functions.checkThatHelpIsThere
|
||||||
|
import org.ligi.passandroid.functions.expand
|
||||||
|
import org.ligi.passandroid.functions.isCollapsed
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
|
||||||
|
@TargetApi(14)
|
||||||
|
class ThePassListActivity {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskActivityRule(PassListActivity::class.java) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testListIsThere() {
|
||||||
|
|
||||||
|
onView(withId(pass_recyclerview)).check(matches(isDisplayed()))
|
||||||
|
rule.screenShot("list")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testHelpMenuBringsUsToHelp() {
|
||||||
|
onView(withId(R.id.menu_help)).perform(click())
|
||||||
|
|
||||||
|
checkThatHelpIsThere()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCloseFabOnBackPressed() {
|
||||||
|
onView(withId(R.id.fam)).perform(expand())
|
||||||
|
|
||||||
|
pressBack()
|
||||||
|
|
||||||
|
onView(withId(R.id.fam))
|
||||||
|
.check(matches(isDisplayed()))
|
||||||
|
.check(matches(isCollapsed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOpenVisibleOn19plus() {
|
||||||
|
onView(withId(R.id.fam)).perform(expand())
|
||||||
|
|
||||||
|
pressBack()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 19) {
|
||||||
|
onView(withId(R.id.fab_action_open_file)).check(matches(isDisplayed()))
|
||||||
|
} else {
|
||||||
|
onView(withId(R.id.fab_action_open_file)).check(matches(withEffectiveVisibility(Visibility.GONE)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,34 +1,25 @@
|
||||||
package org.ligi.passandroid
|
package org.ligi.passandroid
|
||||||
|
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import android.support.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.action.ViewActions.typeText
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.action.ViewActions.replaceText
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.rule.ActivityTestRule
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.ligi.passandroid.helper.ScreenshotTaker
|
|
||||||
import org.ligi.passandroid.model.PassStore
|
|
||||||
import org.ligi.passandroid.ui.PassListActivity
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
import org.ligi.passandroid.ui.PassListFragment
|
import org.ligi.passandroid.ui.PassListFragment
|
||||||
import javax.inject.Inject
|
import org.ligi.trulesk.TruleskIntentRule
|
||||||
|
|
||||||
class ThePassListSwiping : BaseUnitTest() {
|
const val CUSTOM_PROBE = "FOO_PROBE"
|
||||||
|
|
||||||
|
class ThePassListSwiping {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val rule: ActivityTestRule<PassListActivity> = ActivityTestRule(PassListActivity::class.java, true, false)
|
val rule = TruleskIntentRule(PassListActivity::class.java) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
@Inject
|
|
||||||
lateinit var passStore: PassStore
|
|
||||||
|
|
||||||
private val activity by lazy {
|
|
||||||
val build = DaggerTestComponent.create()
|
|
||||||
build.inject(this)
|
|
||||||
App.setComponent(build)
|
|
||||||
rule.launchActivity(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -37,7 +28,7 @@ class ThePassListSwiping : BaseUnitTest() {
|
||||||
|
|
||||||
onView(withText(R.string.topic_trash)).perform(click())
|
onView(withText(R.string.topic_trash)).perform(click())
|
||||||
|
|
||||||
assertThat(passStore.classifier.getTopics()).containsExactly(activity.getString(R.string.topic_trash))
|
assertThat(TestApp.passStore.classifier.getTopics()).containsExactly(rule.activity.getString(R.string.topic_trash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,20 +38,19 @@ class ThePassListSwiping : BaseUnitTest() {
|
||||||
|
|
||||||
onView(withText(R.string.topic_archive)).perform(click())
|
onView(withText(R.string.topic_archive)).perform(click())
|
||||||
|
|
||||||
assertThat(passStore.classifier.getTopics()).containsExactly(activity.getString(R.string.topic_archive))
|
assertThat(TestApp.passStore.classifier.getTopics()).containsExactly(rule.activity.getString(R.string.topic_archive))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWeCanMoveToCustom() {
|
fun testWeCanMoveToCustom() {
|
||||||
val CUSTOM_PROBE = "FOO_PROBE"
|
|
||||||
|
|
||||||
fakeSwipeLeft()
|
fakeSwipeLeft()
|
||||||
|
|
||||||
onView(withId(R.id.new_topic_edit)).perform(typeText(CUSTOM_PROBE))
|
onView(withId(R.id.new_topic_edit)).perform(replaceText(CUSTOM_PROBE))
|
||||||
|
|
||||||
onView(withText(android.R.string.ok)).perform(click())
|
onView(withText(android.R.string.ok)).perform(click())
|
||||||
|
|
||||||
assertThat(passStore.classifier.getTopics()).containsExactly(CUSTOM_PROBE)
|
assertThat(TestApp.passStore.classifier.getTopics()).containsExactly(CUSTOM_PROBE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,7 +65,7 @@ class ThePassListSwiping : BaseUnitTest() {
|
||||||
|
|
||||||
fakeSwipeRight()
|
fakeSwipeRight()
|
||||||
|
|
||||||
ScreenshotTaker.takeScreenshot(activity, "move_to_new_topic_dialog")
|
rule.screenShot("move_to_new_topic_dialog")
|
||||||
|
|
||||||
onView(withText(R.string.move_to_new_topic)).check(matches(isDisplayed()))
|
onView(withText(R.string.move_to_new_topic)).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
@ -86,18 +76,13 @@ class ThePassListSwiping : BaseUnitTest() {
|
||||||
http://stackoverflow.com/questions/35397439/swipe-tests-flaky-on-recyclerview
|
http://stackoverflow.com/questions/35397439/swipe-tests-flaky-on-recyclerview
|
||||||
*/
|
*/
|
||||||
private fun fakeSwipe(dir: Int) {
|
private fun fakeSwipe(dir: Int) {
|
||||||
activity.runOnUiThread {
|
rule.activity.runOnUiThread {
|
||||||
val fragment = activity.supportFragmentManager.fragments.firstOrNull { it is PassListFragment } as PassListFragment
|
val fragment = rule.activity.supportFragmentManager.fragments.firstOrNull { it is PassListFragment } as PassListFragment
|
||||||
fragment.onSwiped(0, dir)
|
fragment.onSwiped(0, dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fakeSwipeRight() {
|
private fun fakeSwipeRight() = fakeSwipe(ItemTouchHelper.RIGHT)
|
||||||
fakeSwipe(ItemTouchHelper.RIGHT)
|
private fun fakeSwipeLeft() = fakeSwipe(ItemTouchHelper.LEFT)
|
||||||
}
|
|
||||||
|
|
||||||
private fun fakeSwipeLeft() {
|
|
||||||
fakeSwipe(ItemTouchHelper.LEFT)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.support.test.filters.MediumTest;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.pass.BarCode;
|
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat;
|
|
||||||
import org.ligi.passandroid.model.pass.PassImpl;
|
|
||||||
import org.ligi.passandroid.ui.PassViewActivity;
|
|
||||||
import org.threeten.bp.ZonedDateTime;
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static org.hamcrest.core.IsNot.not;
|
|
||||||
|
|
||||||
@TargetApi(14)
|
|
||||||
public class ThePassViewActivity extends BaseIntegration<PassViewActivity> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
PassStore passStore;
|
|
||||||
|
|
||||||
PassImpl act_pass;
|
|
||||||
|
|
||||||
public ThePassViewActivity() {
|
|
||||||
super(PassViewActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
final TestComponent newComponent = DaggerTestComponent.create();
|
|
||||||
newComponent.inject(this);
|
|
||||||
App.setComponent(newComponent);
|
|
||||||
|
|
||||||
act_pass = (PassImpl) passStore.getCurrentPass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testThatDescriptionIsThere() {
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withText(act_pass.getDescription())).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testDateIsGoneWhenPassbookHasNoDate() {
|
|
||||||
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.date)).check(matches(not(isDisplayed())));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testDateIsThereWhenPassbookHasDate() {
|
|
||||||
act_pass.setCalendarTimespan(new PassImpl.TimeSpan(ZonedDateTime.now(), null, null));
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.date)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testLinkToCalendarIsThereWhenPassbookHasDate() {
|
|
||||||
act_pass.setCalendarTimespan(new PassImpl.TimeSpan(ZonedDateTime.now(), null, null));
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.addCalendar)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testClickOnCalendarWithExpirationDateGivesWarning() {
|
|
||||||
final ArrayList<PassImpl.TimeSpan> validTimespans = new ArrayList<>();
|
|
||||||
validTimespans.add(new PassImpl.TimeSpan(null, ZonedDateTime.now().minusHours(12), null));
|
|
||||||
act_pass.setValidTimespans(validTimespans);
|
|
||||||
act_pass.setCalendarTimespan(null);
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.addCalendar)).perform(click());
|
|
||||||
|
|
||||||
onView(withText(R.string.expiration_date_to_calendar_warning_message)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testThatTheDialogCanBeDismissed() {
|
|
||||||
testClickOnCalendarWithExpirationDateGivesWarning();
|
|
||||||
|
|
||||||
onView(withText(android.R.string.cancel)).perform(click());
|
|
||||||
|
|
||||||
onView(withText(R.string.expiration_date_to_calendar_warning_message)).check(doesNotExist());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testLinkToCalendarIsNotThereWhenPassbookHasNoDate() {
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.addCalendar)).check(matches(not(isDisplayed())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testClickOnBarcodeOpensFullscreenImage() {
|
|
||||||
getActivity();
|
|
||||||
onView(withId(R.id.barcode_img)).perform(click());
|
|
||||||
|
|
||||||
onView(withId(R.id.fullscreen_barcode)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testZoomControlsAreThereWithBarcode() {
|
|
||||||
act_pass.setBarCode(new BarCode(PassBarCodeFormat.AZTEC, "foo"));
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.zoomIn)).check(matches(isDisplayed()));
|
|
||||||
onView(withId(R.id.zoomIn)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testZoomControlsAreGoneWithoutBarcode() {
|
|
||||||
act_pass.setBarCode(null);
|
|
||||||
getActivity();
|
|
||||||
|
|
||||||
onView(withId(R.id.zoomIn)).check(matches(not(isDisplayed())));
|
|
||||||
onView(withId(R.id.zoomIn)).check(matches(not(isDisplayed())));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import org.hamcrest.core.IsNot.not
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.pass.BarCode
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.model.pass.PassLocation
|
||||||
|
import org.ligi.passandroid.ui.PassViewActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
import org.threeten.bp.ZonedDateTime
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@TargetApi(14)
|
||||||
|
class ThePassViewActivity {
|
||||||
|
|
||||||
|
private fun getActPass() = TestApp.passStore.currentPass as PassImpl
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskActivityRule(PassViewActivity::class.java, false)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testThatDescriptionIsThere() {
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withText(getActPass().description)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDateIsGoneWhenPassbookHasNoDate() {
|
||||||
|
getActPass().validTimespans = ArrayList()
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withId(R.id.date)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEverythingWorksWhenWeHaveSomeLocation() {
|
||||||
|
val timeSpen = ArrayList<PassLocation>()
|
||||||
|
timeSpen.add(PassLocation())
|
||||||
|
getActPass().locations = timeSpen
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withId(R.id.date)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDateIsThereWhenPassbookHasDate() {
|
||||||
|
getActPass().calendarTimespan = PassImpl.TimeSpan(ZonedDateTime.now(), null, null)
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withId(R.id.date)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLinkToCalendarIsThereWhenPassbookHasDate() {
|
||||||
|
getActPass().calendarTimespan = PassImpl.TimeSpan(ZonedDateTime.now(), null, null)
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withText(R.string.pass_to_calendar)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClickOnCalendarWithExpirationDateGivesWarning() {
|
||||||
|
val validTimespans = ArrayList<PassImpl.TimeSpan>()
|
||||||
|
validTimespans.add(PassImpl.TimeSpan(null, ZonedDateTime.now().minusHours(12), null))
|
||||||
|
getActPass().validTimespans = validTimespans
|
||||||
|
getActPass().calendarTimespan = null
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withText(R.string.pass_to_calendar)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.expiration_date_to_calendar_warning_message)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testThatTheDialogCanBeDismissed() {
|
||||||
|
testClickOnCalendarWithExpirationDateGivesWarning()
|
||||||
|
|
||||||
|
onView(withText(android.R.string.cancel)).perform(click())
|
||||||
|
|
||||||
|
onView(withText(R.string.expiration_date_to_calendar_warning_message)).check(doesNotExist())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLinkToCalendarIsNotThereWhenPassbookHasNoDate() {
|
||||||
|
getActPass().validTimespans = ArrayList()
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withText(R.string.pass_to_calendar)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClickOnBarcodeOpensFullscreenImage() {
|
||||||
|
getActPass().barCode = BarCode(PassBarCodeFormat.QR_CODE, "foo")
|
||||||
|
rule.launchActivity(null)
|
||||||
|
onView(withId(R.id.barcode_img)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.fullscreen_barcode)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testZoomControlsAreThereWithBarcode() {
|
||||||
|
getActPass().barCode = BarCode(PassBarCodeFormat.AZTEC, "foo")
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withId(R.id.zoomIn)).check(matches(isDisplayed()))
|
||||||
|
onView(withId(R.id.zoomIn)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testZoomControlsAreGoneWithoutBarcode() {
|
||||||
|
getActPass().barCode = null
|
||||||
|
rule.launchActivity(null)
|
||||||
|
|
||||||
|
onView(withId(R.id.zoomIn)).check(matches(not(isDisplayed())))
|
||||||
|
onView(withId(R.id.zoomIn)).check(matches(not(isDisplayed())))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import org.hamcrest.CoreMatchers.not
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.model.pass.PassLocation
|
||||||
|
import org.ligi.passandroid.ui.PassListActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
import org.threeten.bp.ZoneId
|
||||||
|
import org.threeten.bp.ZonedDateTime
|
||||||
|
|
||||||
|
class ThePassViewHolder {
|
||||||
|
|
||||||
|
private val currentPass by lazy {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
TestApp.passStore.currentPass as PassImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskActivityRule(PassListActivity::class.java, false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun locationButtonShouldBeVisibleIfWeHaveALocation() {
|
||||||
|
|
||||||
|
currentPass.locations = listOf(PassLocation())
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.locationButton)).check(ViewAssertions.matches(isDisplayed()))
|
||||||
|
|
||||||
|
rule.screenShot("with_location")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun locationButtonShouldNotShowIfWeHaveNoLocation() {
|
||||||
|
|
||||||
|
currentPass.locations = listOf()
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.locationButton)).check(ViewAssertions.matches(not(isDisplayed())))
|
||||||
|
|
||||||
|
rule.screenShot("no_location")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dateButtonShouldBeVisibleIfWeHaveADate() {
|
||||||
|
currentPass.calendarTimespan = PassImpl.TimeSpan(ZonedDateTime.of(2016, 11, 23, 20, 42, 42, 5, ZoneId.systemDefault()))
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).check(ViewAssertions.matches(isDisplayed()))
|
||||||
|
|
||||||
|
rule.screenShot("with_date")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dateButtonShouldNotBeVisibleIfWeHaveNoDate() {
|
||||||
|
currentPass.calendarTimespan = null
|
||||||
|
|
||||||
|
rule.launchActivity()
|
||||||
|
|
||||||
|
onView(withId(R.id.timeButton)).check(ViewAssertions.matches(not(isDisplayed())))
|
||||||
|
|
||||||
|
rule.screenShot("no_date")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.support.test.filters.SmallTest;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.ligi.passandroid.model.PastLocationsStore;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
public class ThePastLocationsStore extends BaseIntegration<Activity> {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
Tracker tracker;
|
|
||||||
|
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
public ThePastLocationsStore() {
|
|
||||||
super(Activity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
App.setComponent(DaggerTestComponent.builder().testModule(new TestModule()).build());
|
|
||||||
sharedPreferences = getInstrumentation().getContext().getSharedPreferences("" + (System.currentTimeMillis() / 100000), Context.MODE_PRIVATE);
|
|
||||||
((TestComponent) App.component()).inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tearDown() throws Exception {
|
|
||||||
sharedPreferences.edit().clear().apply();
|
|
||||||
super.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testPastLocationsStoreShouldNeverContainMoreThanMaxElements() {
|
|
||||||
PastLocationsStore tested = new PastLocationsStore(sharedPreferences, tracker);
|
|
||||||
|
|
||||||
for (int i = 0; i < PastLocationsStore.MAX_ELEMENTS * 2; i++) {
|
|
||||||
tested.putLocation("" + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(tested.getLocations().size()).isEqualTo(PastLocationsStore.MAX_ELEMENTS);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
public void testPastLocationsStoreShouldStoreOnlyOneOfAKind() {
|
|
||||||
PastLocationsStore tested = new PastLocationsStore(sharedPreferences, tracker);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
tested.putLocation("foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(tested.getLocations()).containsOnly("foo");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.PastLocationsStore
|
||||||
|
import org.ligi.passandroid.ui.PassViewActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
class ThePastLocationsStore {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var rule = TruleskActivityRule(PassViewActivity::class.java) {
|
||||||
|
TestApp.populatePassStoreWithSinglePass()
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var tracker: Tracker
|
||||||
|
|
||||||
|
private val prefs: SharedPreferences by lazy { InstrumentationRegistry.getInstrumentation().context.getSharedPreferences("" + System.currentTimeMillis() / 100000, Context.MODE_PRIVATE) }
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
prefs.edit().clear().apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPastLocationsStoreShouldNeverContainMoreThanMaxElements() {
|
||||||
|
val tested = PastLocationsStore(prefs, tracker)
|
||||||
|
|
||||||
|
for (i in 0 until PastLocationsStore.MAX_ELEMENTS * 2) {
|
||||||
|
tested.putLocation("" + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(tested.locations.size).isEqualTo(PastLocationsStore.MAX_ELEMENTS)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPastLocationsStoreShouldStoreOnlyOneOfAKind() {
|
||||||
|
val tested = PastLocationsStore(prefs, tracker)
|
||||||
|
|
||||||
|
for (i in 0..2) {
|
||||||
|
tested.putLocation("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(tested.locations).containsOnly("foo")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import com.linkedin.android.testbutler.TestButler
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.ligi.passandroid.model.AndroidSettings
|
||||||
|
import org.ligi.passandroid.model.comparator.PassByTimeComparator
|
||||||
|
import org.ligi.passandroid.model.comparator.PassByTypeFirstAndTimeSecondComparator
|
||||||
|
import org.ligi.passandroid.model.comparator.PassTemporalDistanceComparator
|
||||||
|
import org.ligi.passandroid.ui.PreferenceActivity
|
||||||
|
import org.ligi.trulesk.TruleskActivityRule
|
||||||
|
|
||||||
|
class ThePreferenceActivity {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val rule = TruleskActivityRule(PreferenceActivity::class.java) {
|
||||||
|
TestButler.grantPermission(ApplicationProvider.getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
TestButler.grantPermission(ApplicationProvider.getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val androidSettings by lazy { AndroidSettings(rule.activity) }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun autoLightToggles() {
|
||||||
|
rule.screenShot("preferences")
|
||||||
|
|
||||||
|
val automaticLightEnabled = androidSettings.isAutomaticLightEnabled()
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_autolight_title)).perform(click())
|
||||||
|
|
||||||
|
assertThat(automaticLightEnabled).isEqualTo(!androidSettings.isAutomaticLightEnabled())
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_autolight_title)).perform(click())
|
||||||
|
|
||||||
|
assertThat(automaticLightEnabled).isEqualTo(androidSettings.isAutomaticLightEnabled())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun condensedToggles() {
|
||||||
|
|
||||||
|
val condensedModeEnabled = androidSettings.isCondensedModeEnabled()
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_condensed_title)).perform(click())
|
||||||
|
|
||||||
|
assertThat(condensedModeEnabled).isEqualTo(!androidSettings.isCondensedModeEnabled())
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_condensed_title)).perform(click())
|
||||||
|
|
||||||
|
assertThat(condensedModeEnabled).isEqualTo(androidSettings.isCondensedModeEnabled())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun weCanSetAllSortOrders() {
|
||||||
|
|
||||||
|
val resources = rule.activity.resources
|
||||||
|
val sortOrders = resources.getStringArray(R.array.sort_orders)
|
||||||
|
sortOrders.forEach { sortOrder ->
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_sort_title)).perform(click())
|
||||||
|
onView(withText(sortOrder)).perform(click())
|
||||||
|
|
||||||
|
assertThat(androidSettings.getSortOrder().toComparator()).isInstanceOf(when (sortOrder) {
|
||||||
|
resources.getString(R.string.sort_order_date_asc) -> PassByTimeComparator::class.java
|
||||||
|
resources.getString(R.string.sort_order_date_desc) -> PassByTimeComparator::class.java
|
||||||
|
resources.getString(R.string.sort_order_date_type) -> PassByTypeFirstAndTimeSecondComparator::class.java
|
||||||
|
resources.getString(R.string.sort_order_date_temporaldistance) -> PassTemporalDistanceComparator::class.java
|
||||||
|
else -> throw RuntimeException("unexpected sort order")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun weCanSetAllNightModes() {
|
||||||
|
|
||||||
|
val resources = rule.activity.resources
|
||||||
|
val sortOrders = resources.getStringArray(R.array.night_modes)
|
||||||
|
|
||||||
|
sortOrders.filterNot { Build.VERSION.SDK_INT >= 21 && it == resources.getString(R.string.night_mode_auto) }.forEach { sortOrder ->
|
||||||
|
|
||||||
|
onView(withText(R.string.preference_daynight_title)).perform(click())
|
||||||
|
onView(withText(sortOrder)).perform(click())
|
||||||
|
|
||||||
|
assertThat(androidSettings.getNightMode()).isEqualTo(when (sortOrder) {
|
||||||
|
resources.getString(R.string.night_mode_day) -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
|
resources.getString(R.string.night_mode_night) -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
resources.getString(R.string.night_mode_auto) -> AppCompatDelegate.MODE_NIGHT_AUTO
|
||||||
|
else -> throw RuntimeException("unexpected night-mode")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,15 @@
|
||||||
package org.ligi.passandroid
|
package org.ligi.passandroid
|
||||||
|
|
||||||
import android.support.test.runner.AndroidJUnit4
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.ligi.passandroid.functions.loadPassFromAsset
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
class TheQuirkCorrector {
|
||||||
class TheQuirkCorrector : TheAppleStyleBarcodeReaderBase() {
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWestbahnDescriptionIsFixed() {
|
fun testWestbahnDescriptionIsFixed() {
|
||||||
loadPassFromAsset("passes/workarounds/westbahn/special.pkpass") {
|
loadPassFromAsset("passes/workarounds/westbahn/special.pkpass") {
|
||||||
assertThat(it.description).isEqualTo("Wien Westbahnhof->Amstetten")
|
assertThat(it!!.description).isEqualTo("Wien Westbahnhof->Amstetten")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.ligi.passandroid;
|
package org.ligi.passandroid;
|
||||||
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -27,7 +27,7 @@ public class TheUnzipPassController {
|
||||||
PassStore passStore;
|
PassStore passStore;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class TheUnzipPassController {
|
||||||
verify(failCallback).fail(any(String.class));
|
verify(failCallback).fail(any(String.class));
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
fail("should be able to load file");
|
fail("should be able to load file " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import com.google.zxing.BinaryBitmap
|
||||||
|
import com.google.zxing.MultiFormatReader
|
||||||
|
import com.google.zxing.RGBLuminanceSource
|
||||||
|
import com.google.zxing.common.HybridBinarizer
|
||||||
|
|
||||||
|
fun Bitmap.decodeBarCode(): String {
|
||||||
|
val intArray = IntArray(this.width * this.height)
|
||||||
|
this.getPixels(intArray, 0, this.width, 0, 0, this.width, this.height)
|
||||||
|
|
||||||
|
val source = RGBLuminanceSource(this.width, this.height, intArray)
|
||||||
|
val bitmap = BinaryBitmap(HybridBinarizer(source))
|
||||||
|
|
||||||
|
val reader = MultiFormatReader()// use this otherwise ChecksumException
|
||||||
|
|
||||||
|
val result = reader.decode(bitmap)
|
||||||
|
return result.text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import androidx.test.espresso.UiController
|
||||||
|
import androidx.test.espresso.ViewAction
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||||
|
import android.view.View
|
||||||
|
import net.i2p.android.ext.floatingactionbutton.FloatingActionsMenu
|
||||||
|
import org.hamcrest.Description
|
||||||
|
import org.hamcrest.Matcher
|
||||||
|
import org.hamcrest.TypeSafeMatcher
|
||||||
|
|
||||||
|
fun expand(): ViewAction? = ExpandFabAction()
|
||||||
|
|
||||||
|
class ExpandFabAction : ViewAction {
|
||||||
|
|
||||||
|
override fun getConstraints(): Matcher<View> = isAssignableFrom(FloatingActionsMenu::class.java)
|
||||||
|
|
||||||
|
override fun getDescription() = "expands the floating action menu"
|
||||||
|
|
||||||
|
override fun perform(uiController: UiController?, view: View?) {
|
||||||
|
val fam = view as FloatingActionsMenu
|
||||||
|
fam.expand()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CollapsedCheck : TypeSafeMatcher<FloatingActionsMenu>(FloatingActionsMenu::class.java) {
|
||||||
|
|
||||||
|
override fun describeTo(description: Description?) {
|
||||||
|
description?.appendText("is in collapsed state")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun matchesSafely(fam: FloatingActionsMenu?): Boolean {
|
||||||
|
return !fam?.isExpanded!!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
|
||||||
|
fun checkThatHelpIsThere() {
|
||||||
|
onView(withId(R.id.help_text)).check(matches(isDisplayed()))
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import org.hamcrest.Matcher
|
||||||
|
|
||||||
|
fun isCollapsed(): Matcher<in View>? = CollapsedCheck() as Matcher<in View>
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.assertj.core.api.Fail.fail
|
||||||
|
import org.ligi.passandroid.TestApp
|
||||||
|
import org.ligi.passandroid.model.InputStreamWithSource
|
||||||
|
import org.ligi.passandroid.model.PassStore
|
||||||
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
|
import org.ligi.passandroid.reader.AppleStylePassReader
|
||||||
|
import org.ligi.passandroid.ui.UnzipPassController
|
||||||
|
import org.ligi.passandroid.ui.UnzipPassController.FailCallback
|
||||||
|
import org.ligi.passandroid.ui.UnzipPassController.InputStreamUnzipControllerSpec
|
||||||
|
import org.mockito.Mockito.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
private fun getTestTargetPath(context: Context) = File(context.cacheDir, "test_passes")
|
||||||
|
|
||||||
|
|
||||||
|
fun loadPassFromAsset(asset: String, callback: (pass: Pass?) -> Unit) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
val instrumentation = InstrumentationRegistry.getInstrumentation()
|
||||||
|
val inputStream = instrumentation.context.resources.assets.open(asset)
|
||||||
|
val inputStreamWithSource = InputStreamWithSource("none", inputStream)
|
||||||
|
|
||||||
|
val mock = mock(FailCallback::class.java)
|
||||||
|
val spec = InputStreamUnzipControllerSpec(inputStreamWithSource, instrumentation.targetContext, mock(
|
||||||
|
PassStore::class.java),
|
||||||
|
object : UnzipPassController.SuccessCallback {
|
||||||
|
override fun call(uuid: String) {
|
||||||
|
callback.invoke(AppleStylePassReader.read(File(getTestTargetPath(instrumentation.targetContext), uuid), "en",
|
||||||
|
instrumentation.targetContext,TestApp.tracker))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mock
|
||||||
|
)
|
||||||
|
|
||||||
|
spec.overwrite = true
|
||||||
|
spec.targetPath = getTestTargetPath(spec.context)
|
||||||
|
UnzipPassController.processInputStream(spec)
|
||||||
|
|
||||||
|
verify(mock, never()).fail(anyString())
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
fail("should be able to load file ", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
package org.ligi.passandroid.helper
|
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.google.zxing.BinaryBitmap
|
|
||||||
import com.google.zxing.MultiFormatReader
|
|
||||||
import com.google.zxing.RGBLuminanceSource
|
|
||||||
import com.google.zxing.common.HybridBinarizer
|
|
||||||
|
|
||||||
object BarcodeDecoder {
|
|
||||||
|
|
||||||
fun decodeBitmap(bMap: Bitmap): String {
|
|
||||||
val intArray = IntArray(bMap.width * bMap.height)
|
|
||||||
bMap.getPixels(intArray, 0, bMap.width, 0, 0, bMap.width, bMap.height)
|
|
||||||
|
|
||||||
val source = RGBLuminanceSource(bMap.width, bMap.height, intArray)
|
|
||||||
val bitmap = BinaryBitmap(HybridBinarizer(source))
|
|
||||||
|
|
||||||
val reader = MultiFormatReader()// use this otherwise ChecksumException
|
|
||||||
|
|
||||||
val result = reader.decode(bitmap)
|
|
||||||
return result.text
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package org.ligi.passandroid.helper
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import com.jraska.falcon.FalconSpoon
|
|
||||||
|
|
||||||
/*
|
|
||||||
Facade for screenshot's
|
|
||||||
*/
|
|
||||||
object ScreenshotTaker {
|
|
||||||
|
|
||||||
fun takeScreenshot(activity: Activity, message: String) {
|
|
||||||
FalconSpoon.screenshot(activity, message)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +1,47 @@
|
||||||
package org.ligi.passandroid.injections
|
package org.ligi.passandroid.injections
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.BroadcastChannel
|
||||||
|
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||||
import org.ligi.passandroid.model.PassClassifier
|
import org.ligi.passandroid.model.PassClassifier
|
||||||
import org.ligi.passandroid.model.PassStore
|
import org.ligi.passandroid.model.PassStore
|
||||||
|
import org.ligi.passandroid.model.PassStoreUpdateEvent
|
||||||
import org.ligi.passandroid.model.pass.Pass
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class FixedPassListPassStore(private val passes: List<Pass>) : PassStore {
|
class FixedPassListPassStore(private var passes: List<Pass>) : PassStore {
|
||||||
|
|
||||||
|
override lateinit var classifier: PassClassifier
|
||||||
|
|
||||||
|
init {
|
||||||
|
classifier = PassClassifier(HashMap(), this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setList(newPasses: List<Pass>, newCurrentPass: Pass? = newPasses.firstOrNull()) {
|
||||||
|
currentPass = newCurrentPass
|
||||||
|
passes = newPasses
|
||||||
|
passMap.clear()
|
||||||
|
passMap.putAll(createHashMap())
|
||||||
|
|
||||||
|
classifier = PassClassifier(HashMap(), this)
|
||||||
|
}
|
||||||
|
|
||||||
override var currentPass: Pass? = null
|
override var currentPass: Pass? = null
|
||||||
|
|
||||||
override val passMap: Map<String, Pass> by lazy {
|
override val passMap: HashMap<String, Pass> by lazy {
|
||||||
|
return@lazy createHashMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createHashMap(): HashMap<String, Pass> {
|
||||||
val hashMap = HashMap<String, Pass>()
|
val hashMap = HashMap<String, Pass>()
|
||||||
|
|
||||||
passes.forEach { hashMap.put(it.id, it) }
|
passes.forEach { hashMap[it.id] = it }
|
||||||
return@lazy hashMap
|
return hashMap
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPassbookForId(id: String): Pass? {
|
override fun getPassbookForId(id: String): Pass? {
|
||||||
return passMap[id]
|
return passMap[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
override val classifier: PassClassifier by lazy {
|
|
||||||
PassClassifier(HashMap<String, String>(), this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deletePassWithId(id: String): Boolean {
|
override fun deletePassWithId(id: String): Boolean {
|
||||||
return false
|
return false
|
||||||
|
@ -32,6 +51,8 @@ class FixedPassListPassStore(private val passes: List<Pass>) : PassStore {
|
||||||
return File("")
|
return File("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val updateChannel: BroadcastChannel<PassStoreUpdateEvent> = ConflatedBroadcastChannel()
|
||||||
|
|
||||||
override fun save(pass: Pass) {
|
override fun save(pass: Pass) {
|
||||||
// no effect in this impl
|
// no effect in this impl
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
package org.ligi.passandroid.reporting
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Instrumentation
|
|
||||||
import android.support.test.espresso.FailureHandler
|
|
||||||
import android.support.test.espresso.base.DefaultFailureHandler
|
|
||||||
import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
|
|
||||||
import android.support.test.runner.lifecycle.Stage
|
|
||||||
import android.view.View
|
|
||||||
import org.hamcrest.Matcher
|
|
||||||
import org.ligi.passandroid.helper.ScreenshotTaker
|
|
||||||
|
|
||||||
|
|
||||||
class SpooningFailureHandler(private val instrumentation: Instrumentation) : FailureHandler {
|
|
||||||
|
|
||||||
private val delegate by lazy { DefaultFailureHandler(instrumentation.targetContext) }
|
|
||||||
|
|
||||||
|
|
||||||
override fun handle(error: Throwable, viewMatcher: Matcher<View>) {
|
|
||||||
try {
|
|
||||||
ScreenshotTaker.takeScreenshot(currentActivity, "error_falcon")
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
throwable.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate.handle(error, viewMatcher)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private val currentActivity: Activity
|
|
||||||
@Throws(Throwable::class)
|
|
||||||
get() {
|
|
||||||
instrumentation.waitForIdleSync()
|
|
||||||
val activity = arrayOfNulls<Activity>(1)
|
|
||||||
instrumentation.runOnMainSync {
|
|
||||||
val activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED)
|
|
||||||
activity[0] = activities.iterator().next()
|
|
||||||
}
|
|
||||||
return activity[0]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package org.ligi.passandroid.steps;
|
|
||||||
|
|
||||||
import org.ligi.passandroid.R;
|
|
||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
|
||||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
|
||||||
|
|
||||||
public class HelpSteps {
|
|
||||||
|
|
||||||
public static void checkThatHelpIsThere() {
|
|
||||||
onView(withId(R.id.help_text)).check(matches(isDisplayed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
5
android/src/forAmazon/res/values/strings.xml
Normal file
5
android/src/forAmazon/res/values/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="market_url" translatable="false">amzn://apps/android?p=%s</string>
|
||||||
|
<string name="nav_market" translatable="false">Play</string>
|
||||||
|
</resources>
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="market_url">https://f-droid.org/repository/browse/?fdid=%s</string>
|
<string name="market_url" translatable="false">https://f-droid.org/repository/browse/?fdid=%s</string>
|
||||||
</resources>
|
</resources>
|
|
@ -11,6 +11,7 @@
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
|
<uses-permission-sdk-23 android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
|
||||||
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".InstallListener"
|
android:name=".InstallListener"
|
||||||
|
android:permission="android.permission.INSTALL_PACKAGES"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.android.vending.INSTALL_REFERRER"/>
|
<action android:name="com.android.vending.INSTALL_REFERRER"/>
|
||||||
|
@ -35,21 +37,21 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="@string/authority_fileprovider"
|
android:authorities="@string/authority_fileprovider"
|
||||||
android:grantUriPermissions="true"
|
android:grantUriPermissions="true"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/filepaths" />
|
android:resource="@xml/filepaths"/>
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<service android:name=".ui.SearchPassesIntentService"/>
|
<service android:name=".scan.SearchPassesIntentService"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.PassListActivity"
|
android:name=".ui.PassListActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppBaseThemeNoActionbar">
|
android:theme="@style/AppThemeNoActionbar">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
@ -62,7 +64,7 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.HelpActivity"
|
android:name=".ui.HelpActivity"
|
||||||
android:theme="@style/AppBaseThemeNoActionbar"/>
|
android:theme="@style/AppThemeNoActionbar"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.PassEditActivity"/>
|
android:name=".ui.PassEditActivity"/>
|
||||||
<activity
|
<activity
|
||||||
|
@ -121,39 +123,35 @@
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
<data android:mimeType="application/vnd.espass-espass"/>
|
<data android:scheme="http" android:mimeType="application/vnd.espass-espass"/>
|
||||||
</intent-filter>
|
<data android:scheme="https" android:mimeType="application/vnd.espass-espass"/>
|
||||||
<intent-filter>
|
<data android:scheme="content" android:mimeType="application/vnd.espass-espass"/>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<data android:scheme="file" android:mimeType="application/vnd.espass-espass"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<data android:scheme="http" android:mimeType="application/vnd.espass-espass+zip"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<data android:scheme="https" android:mimeType="application/vnd.espass-espass+zip"/>
|
||||||
|
<data android:scheme="content" android:mimeType="application/vnd.espass-espass+zip"/>
|
||||||
|
<data android:scheme="file" android:mimeType="application/vnd.espass-espass+zip"/>
|
||||||
|
|
||||||
<data android:mimeType="application/vnd.espass-espass+zip"/>
|
<data android:scheme="http" android:mimeType="application/vnd.apple.pkpass"/>
|
||||||
</intent-filter>
|
<data android:scheme="https" android:mimeType="application/vnd.apple.pkpass"/>
|
||||||
<intent-filter>
|
<data android:scheme="content" android:mimeType="application/vnd.apple.pkpass"/>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<data android:scheme="file" android:mimeType="application/vnd.apple.pkpass"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<data android:scheme="http" android:mimeType="application/pkpass"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<data android:scheme="https" android:mimeType="application/pkpass"/>
|
||||||
|
<data android:scheme="content" android:mimeType="application/pkpass"/>
|
||||||
|
<data android:scheme="file" android:mimeType="application/pkpass"/>
|
||||||
|
|
||||||
<data android:mimeType="application/vnd.apple.pkpass"/>
|
<data android:scheme="http" android:mimeType="application/vndapplepkpass"/>
|
||||||
</intent-filter>
|
<data android:scheme="https" android:mimeType="application/vndapplepkpass"/>
|
||||||
<intent-filter>
|
<data android:scheme="content" android:mimeType="application/vndapplepkpass"/>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<data android:scheme="file" android:mimeType="application/vndapplepkpass"/>
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<data android:scheme="http" android:mimeType="application/vnd-com.apple.pkpass"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<data android:scheme="https" android:mimeType="application/vnd-com.apple.pkpass"/>
|
||||||
|
<data android:scheme="content" android:mimeType="application/vnd-com.apple.pkpass"/>
|
||||||
<data android:mimeType="application/vndapplepkpass"/>
|
<data android:scheme="file" android:mimeType="application/vnd-com.apple.pkpass"/>
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:mimeType="application/vnd-com.apple.pkpass"/>
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -555,6 +553,460 @@
|
||||||
android:scheme="content"/>
|
android:scheme="content"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- pdf -->
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="content" android:mimeType="application/x-pdf"/>
|
||||||
|
<data android:scheme="content" android:mimeType="application/pdf"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="http"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="https"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="file"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
<data
|
||||||
|
android:host="*"
|
||||||
|
android:mimeType="*/*"
|
||||||
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pdf"
|
||||||
|
android:scheme="content"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<!-- pkpass -->
|
<!-- pkpass -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
@ -600,22 +1052,6 @@
|
||||||
android:pathPattern="/.*\\.pkpass"
|
android:pathPattern="/.*\\.pkpass"
|
||||||
android:scheme="content"/>
|
android:scheme="content"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:mimeType="application/vnd.apple.pkpass"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
|
|
||||||
<data android:mimeType="application/vnd-com.apple.pkpass"/>
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
@ -787,8 +1223,7 @@
|
||||||
android:host="*"
|
android:host="*"
|
||||||
android:pathPattern="/.*\\..*\\..*\\..*\\.pkpass"
|
android:pathPattern="/.*\\..*\\..*\\..*\\.pkpass"
|
||||||
android:scheme="https"/>
|
android:scheme="https"/>
|
||||||
<data
|
<data android:host="*"
|
||||||
android:host="*"
|
|
||||||
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pkpass"
|
android:pathPattern="/.*\\..*\\..*\\..*\\..*\\.pkpass"
|
||||||
android:scheme="https"/>
|
android:scheme="https"/>
|
||||||
<data
|
<data
|
||||||
|
@ -1113,7 +1548,7 @@
|
||||||
<data
|
<data
|
||||||
android:scheme="http"
|
android:scheme="http"
|
||||||
android:host="pass-cloud.appspot.com"
|
android:host="pass-cloud.appspot.com"
|
||||||
/>
|
/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -1141,10 +1576,31 @@
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
<data
|
<data
|
||||||
android:host="services.aircanada.com"
|
android:host="mci.aircanada.com"
|
||||||
android:scheme="http"/>
|
android:scheme="http"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="mci.aircanada.com"
|
||||||
|
android:scheme="https"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="services.aircanada.com"
|
||||||
|
android:scheme="http"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
@ -1242,6 +1698,7 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.quirk_fix.OpenIphoneWebView"/>
|
<activity android:name=".ui.quirk_fix.OpenIphoneWebView"/>
|
||||||
|
<activity android:name=".ui.TouchImageActivity"/>
|
||||||
|
|
||||||
<activity android:name=".ui.quirk_fix.USAirwaysLoadActivity">
|
<activity android:name=".ui.quirk_fix.USAirwaysLoadActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -1335,8 +1792,9 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.PassViewActivity"
|
android:name=".ui.PassViewActivity"
|
||||||
android:theme="@style/AppBaseThemeNoActionbar"
|
android:theme="@style/AppThemeNoActionbar"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:exported="true"
|
||||||
android:parentActivityName=".ui.PassListActivity">
|
android:parentActivityName=".ui.PassListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
@ -1346,6 +1804,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.FullscreenBarcodeActivity"
|
android:name=".ui.FullscreenBarcodeActivity"
|
||||||
android:label="@string/app_name"/>
|
android:label="@string/app_name"/>
|
||||||
|
<activity android:name=".scan.PassScanActivity" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005-2014, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -35,6 +35,7 @@ import java.util.List;
|
||||||
* <p/>
|
* <p/>
|
||||||
* @stable ICU 3.4
|
* @stable ICU 3.4
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public class CharsetDetector {
|
public class CharsetDetector {
|
||||||
|
|
||||||
// Question: Should we have getters corresponding to the setters for input text
|
// Question: Should we have getters corresponding to the setters for input text
|
||||||
|
@ -182,7 +183,7 @@ public class CharsetDetector {
|
||||||
* @stable ICU 3.4
|
* @stable ICU 3.4
|
||||||
*/
|
*/
|
||||||
public CharsetMatch[] detectAll() {
|
public CharsetMatch[] detectAll() {
|
||||||
ArrayList<CharsetMatch> matches = new ArrayList<CharsetMatch>();
|
ArrayList<CharsetMatch> matches = new ArrayList<>();
|
||||||
|
|
||||||
MungeInput(); // Strip html markup, collect byte stats.
|
MungeInput(); // Strip html markup, collect byte stats.
|
||||||
|
|
||||||
|
@ -344,7 +345,7 @@ public class CharsetDetector {
|
||||||
* it by removing what appears to be html markup.
|
* it by removing what appears to be html markup.
|
||||||
*/
|
*/
|
||||||
private void MungeInput() {
|
private void MungeInput() {
|
||||||
int srci = 0;
|
int srci;
|
||||||
int dsti = 0;
|
int dsti = 0;
|
||||||
byte b;
|
byte b;
|
||||||
boolean inMarkup = false;
|
boolean inMarkup = false;
|
||||||
|
@ -473,7 +474,7 @@ public class CharsetDetector {
|
||||||
private static final List<CSRecognizerInfo> ALL_CS_RECOGNIZERS;
|
private static final List<CSRecognizerInfo> ALL_CS_RECOGNIZERS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
List<CSRecognizerInfo> list = new ArrayList<CSRecognizerInfo>();
|
List<CSRecognizerInfo> list = new ArrayList<>();
|
||||||
|
|
||||||
list.add(new CSRecognizerInfo(new CharsetRecog_UTF8(), true));
|
list.add(new CSRecognizerInfo(new CharsetRecog_UTF8(), true));
|
||||||
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_16_BE(), true));
|
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_16_BE(), true));
|
||||||
|
@ -522,7 +523,7 @@ public class CharsetDetector {
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public String[] getDetectableCharsets() {
|
public String[] getDetectableCharsets() {
|
||||||
List<String> csnames = new ArrayList<String>(ALL_CS_RECOGNIZERS.size());
|
List<String> csnames = new ArrayList<>(ALL_CS_RECOGNIZERS.size());
|
||||||
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
|
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
|
||||||
CSRecognizerInfo rcinfo = ALL_CS_RECOGNIZERS.get(i);
|
CSRecognizerInfo rcinfo = ALL_CS_RECOGNIZERS.get(i);
|
||||||
boolean active = (fEnabledRecognizers == null) ? rcinfo.isDefaultEnabled : fEnabledRecognizers[i];
|
boolean active = (fEnabledRecognizers == null) ? rcinfo.isDefaultEnabled : fEnabledRecognizers[i];
|
||||||
|
@ -530,7 +531,7 @@ public class CharsetDetector {
|
||||||
csnames.add(rcinfo.recognizer.getName());
|
csnames.add(rcinfo.recognizer.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return csnames.toArray(new String[csnames.size()]);
|
return csnames.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005-2012, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -26,6 +26,7 @@ import java.io.Reader;
|
||||||
*
|
*
|
||||||
* @stable ICU 3.4
|
* @stable ICU 3.4
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public class CharsetMatch implements Comparable<CharsetMatch> {
|
public class CharsetMatch implements Comparable<CharsetMatch> {
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,13 +86,13 @@ public class CharsetMatch implements Comparable<CharsetMatch> {
|
||||||
* @stable ICU 3.4
|
* @stable ICU 3.4
|
||||||
*/
|
*/
|
||||||
public String getString(int maxLength) throws java.io.IOException {
|
public String getString(int maxLength) throws java.io.IOException {
|
||||||
String result = null;
|
String result;
|
||||||
if (fInputStream != null) {
|
if (fInputStream != null) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
char[] buffer = new char[1024];
|
char[] buffer = new char[1024];
|
||||||
Reader reader = getReader();
|
Reader reader = getReader();
|
||||||
int max = maxLength < 0? Integer.MAX_VALUE : maxLength;
|
int max = maxLength < 0? Integer.MAX_VALUE : maxLength;
|
||||||
int bytesRead = 0;
|
int bytesRead;
|
||||||
|
|
||||||
while ((bytesRead = reader.read(buffer, 0, Math.min(max, 1024))) >= 0) {
|
while ((bytesRead = reader.read(buffer, 0, Math.min(max, 1024))) >= 0) {
|
||||||
sb.append(buffer, 0, bytesRead);
|
sb.append(buffer, 0, bytesRead);
|
||||||
|
@ -108,6 +109,7 @@ public class CharsetMatch implements Comparable<CharsetMatch> {
|
||||||
* be used to open a charset (e.g. IBM424_rtl). The ending '_rtl' or 'ltr'
|
* be used to open a charset (e.g. IBM424_rtl). The ending '_rtl' or 'ltr'
|
||||||
* should be stripped off before creating the string.
|
* should be stripped off before creating the string.
|
||||||
*/
|
*/
|
||||||
|
//noinspection IndexOfReplaceableByContains
|
||||||
int startSuffix = name.indexOf("_rtl") < 0 ? name.indexOf("_ltr") : name.indexOf("_rtl");
|
int startSuffix = name.indexOf("_rtl") < 0 ? name.indexOf("_ltr") : name.indexOf("_rtl");
|
||||||
if (startSuffix > 0) {
|
if (startSuffix > 0) {
|
||||||
name = name.substring(0, startSuffix);
|
name = name.substring(0, startSuffix);
|
||||||
|
@ -234,7 +236,7 @@ public class CharsetMatch implements Comparable<CharsetMatch> {
|
||||||
// If user gave us a byte array, this is it.
|
// If user gave us a byte array, this is it.
|
||||||
private int fRawLength; // Length of data in fRawInput array.
|
private int fRawLength; // Length of data in fRawInput array.
|
||||||
|
|
||||||
private InputStream fInputStream = null; // User's input stream, or null if the user
|
private InputStream fInputStream; // User's input stream, or null if the user
|
||||||
// gave us a byte array.
|
// gave us a byte array.
|
||||||
|
|
||||||
private String fCharsetName; // The name of the charset this CharsetMatch
|
private String fCharsetName; // The name of the charset this CharsetMatch
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/*
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,7 @@ package com.ibm.icu.text;
|
||||||
*
|
*
|
||||||
* The separate classes are nested within this class.
|
* The separate classes are nested within this class.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
abstract class CharsetRecog_2022 extends CharsetRecognizer {
|
abstract class CharsetRecog_2022 extends CharsetRecognizer {
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ abstract class CharsetRecog_2022 extends CharsetRecognizer {
|
||||||
byte [] seq = escapeSequences[escN];
|
byte [] seq = escapeSequences[escN];
|
||||||
|
|
||||||
if ((textLen - i) < seq.length) {
|
if ((textLen - i) < seq.length) {
|
||||||
continue checkEscapes;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j=1; j<seq.length; j++) {
|
for (j=1; j<seq.length; j++) {
|
||||||
|
@ -92,9 +93,6 @@ abstract class CharsetRecog_2022 extends CharsetRecognizer {
|
||||||
return quality;
|
return quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static class CharsetRecog_2022JP extends CharsetRecog_2022 {
|
static class CharsetRecog_2022JP extends CharsetRecog_2022 {
|
||||||
private byte [] [] escapeSequences = {
|
private byte [] [] escapeSequences = {
|
||||||
{0x1b, 0x24, 0x28, 0x43}, // KS X 1001:1992
|
{0x1b, 0x24, 0x28, 0x43}, // KS X 1001:1992
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
/**
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005 - 2014, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Charset recognizer for UTF-8
|
* Charset recognizer for UTF-8
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
class CharsetRecog_UTF8 extends CharsetRecognizer {
|
class CharsetRecog_UTF8 extends CharsetRecognizer {
|
||||||
|
|
||||||
String getName() {
|
String getName() {
|
||||||
|
@ -24,7 +25,7 @@ class CharsetRecog_UTF8 extends CharsetRecognizer {
|
||||||
int numInvalid = 0;
|
int numInvalid = 0;
|
||||||
byte input[] = det.fRawInput;
|
byte input[] = det.fRawInput;
|
||||||
int i;
|
int i;
|
||||||
int trailBytes = 0;
|
int trailBytes;
|
||||||
int confidence;
|
int confidence;
|
||||||
|
|
||||||
if (det.fRawLength >= 3 &&
|
if (det.fRawLength >= 3 &&
|
||||||
|
|
|
@ -12,6 +12,7 @@ package com.ibm.icu.text;
|
||||||
* This class matches UTF-16 and UTF-32, both big- and little-endian. The
|
* This class matches UTF-16 and UTF-32, both big- and little-endian. The
|
||||||
* BOM will be used if it is present.
|
* BOM will be used if it is present.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
abstract class CharsetRecog_Unicode extends CharsetRecognizer {
|
abstract class CharsetRecog_Unicode extends CharsetRecognizer {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
@ -171,7 +172,7 @@ abstract class CharsetRecog_Unicode extends CharsetRecognizer {
|
||||||
{
|
{
|
||||||
int getChar(byte[] input, int index)
|
int getChar(byte[] input, int index)
|
||||||
{
|
{
|
||||||
return (input[index + 0] & 0xFF) << 24 | (input[index + 1] & 0xFF) << 16 |
|
return (input[index] & 0xFF) << 24 | (input[index + 1] & 0xFF) << 16 |
|
||||||
(input[index + 2] & 0xFF) << 8 | (input[index + 3] & 0xFF);
|
(input[index + 2] & 0xFF) << 8 | (input[index + 3] & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ abstract class CharsetRecog_Unicode extends CharsetRecognizer {
|
||||||
int getChar(byte[] input, int index)
|
int getChar(byte[] input, int index)
|
||||||
{
|
{
|
||||||
return (input[index + 3] & 0xFF) << 24 | (input[index + 2] & 0xFF) << 16 |
|
return (input[index + 3] & 0xFF) << 24 | (input[index + 2] & 0xFF) << 16 |
|
||||||
(input[index + 1] & 0xFF) << 8 | (input[index + 0] & 0xFF);
|
(input[index + 1] & 0xFF) << 8 | (input[index] & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getName()
|
String getName()
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
****************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005-2012, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
****************************************************************************
|
*******************************************************************************
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
|
@ -21,6 +20,7 @@ import java.util.Arrays;
|
||||||
* encodings to be checked. The specific encoding being recognized
|
* encodings to be checked. The specific encoding being recognized
|
||||||
* is determined by subclass.
|
* is determined by subclass.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,8 +157,7 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
done = true;
|
done = true;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int byteValue = (int)det.fRawInput[nextIndex++] & 0x00ff;
|
return (int)det.fRawInput[nextIndex++] & 0x00ff;
|
||||||
return byteValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,9 +320,9 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
boolean nextChar(iteratedChar it, CharsetDetector det) {
|
boolean nextChar(iteratedChar it, CharsetDetector det) {
|
||||||
it.index = it.nextIndex;
|
it.index = it.nextIndex;
|
||||||
it.error = false;
|
it.error = false;
|
||||||
int firstByte = 0;
|
int firstByte;
|
||||||
int secondByte = 0;
|
int secondByte;
|
||||||
int thirdByte = 0;
|
int thirdByte;
|
||||||
//int fourthByte = 0;
|
//int fourthByte = 0;
|
||||||
|
|
||||||
buildChar: {
|
buildChar: {
|
||||||
|
@ -372,7 +371,7 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (it.done == false);
|
return (!it.done);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -461,10 +460,10 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
boolean nextChar(iteratedChar it, CharsetDetector det) {
|
boolean nextChar(iteratedChar it, CharsetDetector det) {
|
||||||
it.index = it.nextIndex;
|
it.index = it.nextIndex;
|
||||||
it.error = false;
|
it.error = false;
|
||||||
int firstByte = 0;
|
int firstByte;
|
||||||
int secondByte = 0;
|
int secondByte;
|
||||||
int thirdByte = 0;
|
int thirdByte;
|
||||||
int fourthByte = 0;
|
int fourthByte;
|
||||||
|
|
||||||
buildChar: {
|
buildChar: {
|
||||||
firstByte = it.charValue = it.nextByte(det);
|
firstByte = it.charValue = it.nextByte(det);
|
||||||
|
@ -483,7 +482,7 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
secondByte = it.nextByte(det);
|
secondByte = it.nextByte(det);
|
||||||
it.charValue = (it.charValue << 8) | secondByte;
|
it.charValue = (it.charValue << 8) | secondByte;
|
||||||
|
|
||||||
if (firstByte >= 0x81 && firstByte <= 0xFE) {
|
if (firstByte <= 0xFE) {
|
||||||
// Two byte Char
|
// Two byte Char
|
||||||
if ((secondByte >= 0x40 && secondByte <= 0x7E) || (secondByte >=80 && secondByte <=0xFE)) {
|
if ((secondByte >= 0x40 && secondByte <= 0x7E) || (secondByte >=80 && secondByte <=0xFE)) {
|
||||||
break buildChar;
|
break buildChar;
|
||||||
|
@ -504,11 +503,10 @@ abstract class CharsetRecog_mbcs extends CharsetRecognizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
it.error = true;
|
it.error = true;
|
||||||
break buildChar;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (it.done == false);
|
return !it.done;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int [] commonChars =
|
static int [] commonChars =
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
/*
|
/*
|
||||||
****************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005-2013, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
************************************************************************** *
|
*******************************************************************************
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class recognizes single-byte encodings. Because the encoding scheme is so
|
* This class recognizes single-byte encodings. Because the encoding scheme is so
|
||||||
* simple, language statistics are used to do the matching.
|
* simple, language statistics are used to do the matching.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
abstract class CharsetRecog_sbcs extends CharsetRecognizer {
|
abstract class CharsetRecog_sbcs extends CharsetRecognizer {
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
@ -25,7 +24,7 @@ abstract class CharsetRecog_sbcs extends CharsetRecognizer {
|
||||||
private static final int N_GRAM_MASK = 0xFFFFFF;
|
private static final int N_GRAM_MASK = 0xFFFFFF;
|
||||||
|
|
||||||
protected int byteIndex = 0;
|
protected int byteIndex = 0;
|
||||||
private int ngram = 0;
|
private int ngram;
|
||||||
|
|
||||||
private int[] ngramList;
|
private int[] ngramList;
|
||||||
protected byte[] byteMap;
|
protected byte[] byteMap;
|
||||||
|
@ -161,7 +160,7 @@ abstract class CharsetRecog_sbcs extends CharsetRecognizer {
|
||||||
return (int) (rawPercent * 300.0);
|
return (int) (rawPercent * 300.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class NGramParser_IBM420 extends NGramParser
|
static class NGramParser_IBM420 extends NGramParser
|
||||||
{
|
{
|
||||||
private byte alef = 0x00;
|
private byte alef = 0x00;
|
||||||
|
@ -273,7 +272,7 @@ abstract class CharsetRecog_sbcs extends CharsetRecognizer {
|
||||||
return parser.parse(det, spaceChar);
|
return parser.parse(det, spaceChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
int matchIBM420(CharsetDetector det, int[] ngrams, byte[] byteMap, byte spaceChar){
|
int matchIBM420(CharsetDetector det, int[] ngrams, byte[] byteMap, @SuppressWarnings("SameParameterValue") byte spaceChar){
|
||||||
NGramParser_IBM420 parser = new NGramParser_IBM420(ngrams, byteMap);
|
NGramParser_IBM420 parser = new NGramParser_IBM420(ngrams, byteMap);
|
||||||
return parser.parse(det, spaceChar);
|
return parser.parse(det, spaceChar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/**
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 2005-2012, International Business Machines Corporation and *
|
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
package com.ibm.icu.text;
|
package com.ibm.icu.text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,7 @@ package com.ibm.icu.text;
|
||||||
* The WILL be shared by multiple instances of CharsetDetector.
|
* The WILL be shared by multiple instances of CharsetDetector.
|
||||||
* They encapsulate const charset-specific information.
|
* They encapsulate const charset-specific information.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
abstract class CharsetRecognizer {
|
abstract class CharsetRecognizer {
|
||||||
/**
|
/**
|
||||||
* Get the IANA name of this charset.
|
* Get the IANA name of this charset.
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
|
||||||
import com.jakewharton.threetenabp.AndroidThreeTen;
|
|
||||||
import com.squareup.leakcanary.LeakCanary;
|
|
||||||
import org.ligi.tracedroid.TraceDroid;
|
|
||||||
import org.ligi.tracedroid.logging.Log;
|
|
||||||
|
|
||||||
public class App extends Application {
|
|
||||||
|
|
||||||
private static AppComponent component;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
component = DaggerAppComponent.builder()
|
|
||||||
.appModule(new AppModule(this))
|
|
||||||
.trackerModule(new TrackerModule(this))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
|
||||||
LeakCanary.install(this);
|
|
||||||
AndroidThreeTen.init(this);
|
|
||||||
initTraceDroid();
|
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(component.settings().getNightMode());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initTraceDroid() {
|
|
||||||
TraceDroid.init(this);
|
|
||||||
Log.setTAG("PassAndroid");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AppComponent component() {
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static void setComponent(AppComponent newComponent) {
|
|
||||||
component = newComponent;
|
|
||||||
}
|
|
||||||
}
|
|
63
android/src/main/java/org/ligi/passandroid/App.kt
Normal file
63
android/src/main/java/org/ligi/passandroid/App.kt
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package org.ligi.passandroid
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.android.ext.koin.androidLogger
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import org.koin.core.module.Module
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.ligi.passandroid.json_adapter.ColorAdapter
|
||||||
|
import org.ligi.passandroid.json_adapter.ZonedTimeAdapter
|
||||||
|
import org.ligi.passandroid.model.AndroidFileSystemPassStore
|
||||||
|
import org.ligi.passandroid.model.AndroidSettings
|
||||||
|
import org.ligi.passandroid.model.PassStore
|
||||||
|
import org.ligi.passandroid.model.Settings
|
||||||
|
import org.ligi.passandroid.scan.events.PassScanEventChannelProvider
|
||||||
|
import org.ligi.tracedroid.TraceDroid
|
||||||
|
import org.ligi.tracedroid.logging.Log
|
||||||
|
|
||||||
|
open class App : Application() {
|
||||||
|
|
||||||
|
private val moshi = Moshi.Builder()
|
||||||
|
.add(ZonedTimeAdapter())
|
||||||
|
.add(ColorAdapter())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val settings by lazy { AndroidSettings(this) }
|
||||||
|
|
||||||
|
open fun createKoin(): Module {
|
||||||
|
|
||||||
|
return module {
|
||||||
|
single { AndroidFileSystemPassStore(this@App, get(), moshi) as PassStore }
|
||||||
|
single { settings as Settings }
|
||||||
|
single { createTracker(this@App) }
|
||||||
|
single { PassScanEventChannelProvider() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
startKoin {
|
||||||
|
if (BuildConfig.DEBUG) androidLogger()
|
||||||
|
androidContext(this@App)
|
||||||
|
modules(createKoin())
|
||||||
|
}
|
||||||
|
|
||||||
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
|
AndroidThreeTen.init(this)
|
||||||
|
initTraceDroid()
|
||||||
|
|
||||||
|
AppCompatDelegate.setDefaultNightMode(settings.getNightMode())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun initTraceDroid() {
|
||||||
|
TraceDroid.init(this)
|
||||||
|
Log.setTAG("PassAndroid")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,53 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import dagger.Component;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.Settings;
|
|
||||||
import org.ligi.passandroid.ui.PassAdapter;
|
|
||||||
import org.ligi.passandroid.ui.PassAndroidActivity;
|
|
||||||
import org.ligi.passandroid.ui.PassEditActivity;
|
|
||||||
import org.ligi.passandroid.ui.PassImportActivity;
|
|
||||||
import org.ligi.passandroid.ui.PassListActivity;
|
|
||||||
import org.ligi.passandroid.ui.PassListFragment;
|
|
||||||
import org.ligi.passandroid.ui.PassMenuOptions;
|
|
||||||
import org.ligi.passandroid.ui.PassNavigationView;
|
|
||||||
import org.ligi.passandroid.ui.PassViewActivityBase;
|
|
||||||
import org.ligi.passandroid.ui.SearchPassesIntentService;
|
|
||||||
import org.ligi.passandroid.ui.edit.PassandroidFragment;
|
|
||||||
import org.ligi.passandroid.ui.quirk_fix.USAirwaysLoadActivity;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = {AppModule.class , TrackerModule.class})
|
|
||||||
public interface AppComponent {
|
|
||||||
|
|
||||||
void inject(PassViewActivityBase passViewActivityBase);
|
|
||||||
|
|
||||||
void inject(PassListActivity passListActivity);
|
|
||||||
|
|
||||||
void inject(PassEditActivity passEditActivity);
|
|
||||||
|
|
||||||
void inject(PassandroidFragment passandroidFragment);
|
|
||||||
|
|
||||||
void inject(PassAdapter passAdapter);
|
|
||||||
|
|
||||||
void inject(PassImportActivity passImportActivity);
|
|
||||||
|
|
||||||
void inject(PassMenuOptions passMenuOptions);
|
|
||||||
|
|
||||||
void inject(SearchPassesIntentService searchPassesIntentService);
|
|
||||||
|
|
||||||
void inject(USAirwaysLoadActivity usAirwaysLoadActivity);
|
|
||||||
|
|
||||||
void inject(PassAndroidActivity passAndroidActivity);
|
|
||||||
|
|
||||||
void inject(PassListFragment passListFragment);
|
|
||||||
|
|
||||||
void inject(PassNavigationView passNavigationView);
|
|
||||||
|
|
||||||
PassStore passStore();
|
|
||||||
|
|
||||||
Tracker tracker();
|
|
||||||
|
|
||||||
Settings settings();
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package org.ligi.passandroid;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import com.squareup.moshi.Moshi;
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
|
||||||
import org.ligi.passandroid.json_adapter.ColorAdapter;
|
|
||||||
import org.ligi.passandroid.json_adapter.ZonedTimeAdapter;
|
|
||||||
import org.ligi.passandroid.model.AndroidFileSystemPassStore;
|
|
||||||
import org.ligi.passandroid.model.AndroidSettings;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.Settings;
|
|
||||||
import org.ligi.passandroid.model.State;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class AppModule {
|
|
||||||
|
|
||||||
private final App app;
|
|
||||||
|
|
||||||
public AppModule(App app) {
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
PassStore providePassStore(Settings settings, Moshi moshi,EventBus bus) {
|
|
||||||
return new AndroidFileSystemPassStore(app, settings, moshi,bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
Moshi provideMoshi() {
|
|
||||||
return new Moshi.Builder()
|
|
||||||
.add(new ZonedTimeAdapter())
|
|
||||||
.add(new ColorAdapter())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
Settings provideSettings() {
|
|
||||||
return new AndroidSettings(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
SharedPreferences provideSharedPreferences() {
|
|
||||||
return PreferenceManager.getDefaultSharedPreferences(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
EventBus provideBus() {
|
|
||||||
return EventBus.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
State provideState() {
|
|
||||||
return new State();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.ligi.passandroid;
|
package org.ligi.passandroid;
|
||||||
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
public interface Tracker {
|
public interface Tracker {
|
||||||
void trackException(String s, Throwable e, boolean fatal);
|
void trackException(String s, Throwable e, boolean fatal);
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.ligi.passandroid.actions;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.provider.CalendarContract;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import org.ligi.passandroid.R;
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
import org.threeten.bp.ZonedDateTime;
|
|
||||||
|
|
||||||
public class AddToCalendar {
|
|
||||||
|
|
||||||
public static void tryAddDateToCalendar(final Pass pass, final Activity activity, final ZonedDateTime date) {
|
|
||||||
|
|
||||||
if (pass.getCalendarTimespan() == null) {
|
|
||||||
new AlertDialog.Builder(activity).setMessage(R.string.expiration_date_to_calendar_warning_message)
|
|
||||||
.setTitle(R.string.expiration_date_to_calendar_warning_title)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
reallyAddToCalendar(pass, activity, date);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
reallyAddToCalendar(pass, activity, date);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void reallyAddToCalendar(Pass pass, Activity activity, ZonedDateTime date) {
|
|
||||||
try {
|
|
||||||
final Intent intent = new Intent(Intent.ACTION_EDIT);
|
|
||||||
intent.setType("vnd.android.cursor.item/event");
|
|
||||||
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, date.toEpochSecond() * 1000);
|
|
||||||
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, date.plusHours(1).toEpochSecond() * 1000);
|
|
||||||
intent.putExtra("title", pass.getDescription());
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} catch (ActivityNotFoundException exception) {
|
|
||||||
// TODO maybe action to install calendar app
|
|
||||||
Snackbar.make(activity.getWindow().getDecorView(), R.string.no_calendar_app_found, Snackbar.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package org.ligi.passandroid.events;
|
|
||||||
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
|
|
||||||
public class PassRefreshEvent {
|
|
||||||
public final Pass pass;
|
|
||||||
|
|
||||||
public PassRefreshEvent(Pass pass) {
|
|
||||||
this.pass = pass;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
package org.ligi.passandroid.events
|
|
||||||
|
|
||||||
object PassStoreChangeEvent
|
|
|
@ -1,5 +0,0 @@
|
||||||
package org.ligi.passandroid.events
|
|
||||||
|
|
||||||
import org.ligi.passandroid.model.pass.Pass
|
|
||||||
|
|
||||||
class ScanFinishedEvent(val foundPasses: List<Pass>)
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.ligi.passandroid.events;
|
|
||||||
|
|
||||||
public class ScanProgressEvent {
|
|
||||||
|
|
||||||
public final String message;
|
|
||||||
|
|
||||||
public ScanProgressEvent(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import android.view.View
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
|
||||||
|
const val DEFAULT_EVENT_LENGTH_IN_HOURS = 8L
|
||||||
|
|
||||||
|
fun tryAddDateToCalendar(pass: Pass, contextView: View, timeSpan: PassImpl.TimeSpan) {
|
||||||
|
if (pass.calendarTimespan == null) {
|
||||||
|
AlertDialog.Builder(contextView.context).setMessage(R.string.expiration_date_to_calendar_warning_message)
|
||||||
|
.setTitle(R.string.expiration_date_to_calendar_warning_title)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ -> reallyAddToCalendar(pass, contextView, timeSpan) }
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
reallyAddToCalendar(pass, contextView, timeSpan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reallyAddToCalendar(pass: Pass, contextView: View, timeSpan: PassImpl.TimeSpan) = try {
|
||||||
|
|
||||||
|
val intent = createIntent(pass, timeSpan)
|
||||||
|
contextView.context.startActivity(intent)
|
||||||
|
|
||||||
|
} catch (exception: ActivityNotFoundException) {
|
||||||
|
// TODO maybe action to install calendar app
|
||||||
|
Snackbar.make(contextView, R.string.no_calendar_app_found, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun createIntent(pass: Pass, timeSpan: PassImpl.TimeSpan) = Intent(Intent.ACTION_EDIT).apply {
|
||||||
|
if (timeSpan.from == null && timeSpan.to == null) {
|
||||||
|
throw IllegalArgumentException("span must have either a to or a from")
|
||||||
|
}
|
||||||
|
|
||||||
|
type = "vnd.android.cursor.item/event"
|
||||||
|
val from = timeSpan.from ?: timeSpan.to!!.minusHours(DEFAULT_EVENT_LENGTH_IN_HOURS)
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, from.toEpochSecond() * 1000)
|
||||||
|
|
||||||
|
val to = timeSpan.to ?: timeSpan.from!!.plusHours(DEFAULT_EVENT_LENGTH_IN_HOURS)
|
||||||
|
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, to.toEpochSecond() * 1000)
|
||||||
|
putExtra("title", pass.description)
|
||||||
|
|
||||||
|
|
||||||
|
pass.locations.firstOrNull()?.name?.let {
|
||||||
|
putExtra("eventLocation", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import com.google.zxing.MultiFormatWriter
|
||||||
|
import org.ligi.passandroid.model.pass.PassBarCodeFormat
|
||||||
|
import org.ligi.tracedroid.logging.Log
|
||||||
|
|
||||||
|
|
||||||
|
fun generateBitmapDrawable(resources: Resources, data: String, type: PassBarCodeFormat): BitmapDrawable? {
|
||||||
|
val bitmap = generateBarCodeBitmap(data, type) ?: return null
|
||||||
|
|
||||||
|
return BitmapDrawable(resources, bitmap).apply {
|
||||||
|
isFilterBitmap = false
|
||||||
|
setAntiAlias(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateBarCodeBitmap(data: String, type: PassBarCodeFormat): Bitmap? {
|
||||||
|
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val matrix = getBitMatrix(data, type)
|
||||||
|
val is1D = matrix.height == 1
|
||||||
|
|
||||||
|
// generate an image from the byte matrix
|
||||||
|
val width = matrix.width
|
||||||
|
val height = if (is1D) width / 5 else matrix.height
|
||||||
|
|
||||||
|
// create buffered image to draw to
|
||||||
|
// NTFS Bitmap.Config.ALPHA_8 sounds like an awesome idea - been there - done that ..
|
||||||
|
val barcodeImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
|
||||||
|
|
||||||
|
// iterate through the matrix and draw the pixels to the image
|
||||||
|
for (y in 0 until height) {
|
||||||
|
for (x in 0 until width) {
|
||||||
|
barcodeImage.setPixel(x, y, if (matrix.get(x, if (is1D) 0 else y)) 0 else 0xFFFFFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return barcodeImage
|
||||||
|
} catch (e: com.google.zxing.WriterException) {
|
||||||
|
Log.w("could not write image: $e")
|
||||||
|
// TODO check if we should better return some rescue Image here
|
||||||
|
return null
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Log.w("could not write image: $e")
|
||||||
|
return null
|
||||||
|
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||||
|
// happens for ITF barcode on certain inputs
|
||||||
|
Log.w("could not write image: $e")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBitMatrix(data: String, type: PassBarCodeFormat)
|
||||||
|
= MultiFormatWriter().encode(data, type.zxingBarCodeFormat(), 0, 0)!!
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
import org.ligi.passandroid.model.pass.PassType
|
||||||
|
|
||||||
|
@StringRes
|
||||||
|
fun getHumanCategoryString(fromPass: PassType) = when (fromPass) {
|
||||||
|
PassType.BOARDING -> R.string.boarding_pass
|
||||||
|
PassType.EVENT -> R.string.category_event
|
||||||
|
PassType.COUPON -> R.string.category_coupon
|
||||||
|
PassType.LOYALTY -> R.string.category_storecard
|
||||||
|
PassType.GENERIC -> R.string.category_generic
|
||||||
|
PassType.VOUCHER -> R.string.categories_voucher
|
||||||
|
|
||||||
|
else -> R.string.category_none
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
fun getCategoryDefaultBG(category: PassType) = when (category) {
|
||||||
|
PassType.BOARDING -> 0xFF3d73e9
|
||||||
|
PassType.EVENT -> 0xFF9f3dd0
|
||||||
|
PassType.COUPON -> 0xFF9ccb05
|
||||||
|
PassType.LOYALTY -> 0xFFf29b21
|
||||||
|
PassType.VOUCHER -> 0xFF2A2727
|
||||||
|
PassType.GENERIC -> 0xFFea3c48
|
||||||
|
|
||||||
|
else -> Color.WHITE.toLong()
|
||||||
|
}.toInt()
|
||||||
|
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
fun getCategoryTopImageRes(type: PassType) = when (type) {
|
||||||
|
PassType.BOARDING -> R.drawable.cat_bp
|
||||||
|
PassType.EVENT -> R.drawable.cat_et
|
||||||
|
PassType.COUPON -> R.drawable.cat_cp
|
||||||
|
PassType.LOYALTY -> R.drawable.cat_sc
|
||||||
|
PassType.VOUCHER -> R.drawable.cat_ps
|
||||||
|
PassType.GENERIC -> R.drawable.cat_none
|
||||||
|
|
||||||
|
else -> R.drawable.cat_none
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import org.ligi.passandroid.Tracker
|
||||||
|
import org.ligi.passandroid.model.InputStreamWithSource
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
const val IPHONE_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
|
||||||
|
|
||||||
|
fun fromURI(context: Context, uri: Uri, tracker: Tracker): InputStreamWithSource? {
|
||||||
|
tracker.trackEvent("protocol", "to_inputstream", uri.scheme, null)
|
||||||
|
return when (uri.scheme) {
|
||||||
|
"content" -> fromContent(context, uri)
|
||||||
|
|
||||||
|
"http", "https" ->
|
||||||
|
// TODO check if SPDY should be here
|
||||||
|
return fromOKHttp(uri, tracker)
|
||||||
|
|
||||||
|
"file" -> getDefaultInputStreamForUri(uri)
|
||||||
|
else -> {
|
||||||
|
tracker.trackException("unknown scheme in ImportAsyncTask" + uri.scheme, false)
|
||||||
|
getDefaultInputStreamForUri(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fromOKHttp(uri: Uri, tracker: Tracker): InputStreamWithSource? {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val url = URL(uri.toString())
|
||||||
|
val requestBuilder = Request.Builder().url(url)
|
||||||
|
|
||||||
|
// fake to be an iPhone in some cases when the server decides to send no passbook
|
||||||
|
// to android phones - but only do it then - we are proud to be Android ;-)
|
||||||
|
val iPhoneFakeMap = mapOf(
|
||||||
|
"air_canada" to "//m.aircanada.ca/ebp/",
|
||||||
|
"air_canada2" to "//services.aircanada.com/ebp/",
|
||||||
|
"air_canada3" to "//mci.aircanada.com/mci/bp/",
|
||||||
|
"icelandair" to "//checkin.si.amadeus.net",
|
||||||
|
"mbk" to "//mbk.thy.com/",
|
||||||
|
"heathrow" to "//passbook.heathrow.com/",
|
||||||
|
"eventbrite" to "//www.eventbrite.com/passes/order"
|
||||||
|
)
|
||||||
|
|
||||||
|
for ((key, value) in iPhoneFakeMap) {
|
||||||
|
if (uri.toString().contains(value)) {
|
||||||
|
tracker.trackEvent("quirk_fix", "ua_fake", key, null)
|
||||||
|
requestBuilder.header("User-Agent", IPHONE_USER_AGENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = requestBuilder.build()
|
||||||
|
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
|
val body = response.body()
|
||||||
|
|
||||||
|
if (body != null) {
|
||||||
|
return InputStreamWithSource(uri.toString(), body.byteStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fromContent(ctx: Context, uri: Uri) = ctx.contentResolver.openInputStream(uri)?.let {
|
||||||
|
InputStreamWithSource(uri.toString(), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultInputStreamForUri(uri: Uri) = InputStreamWithSource(uri.toString(), BufferedInputStream(URL(uri.toString()).openStream(), 4096))
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
import org.ligi.passandroid.model.PassClassifier
|
||||||
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
|
|
||||||
|
fun moveWithUndoSnackbar(passClassifier: PassClassifier, pass: Pass, topic: String, activity: Activity) {
|
||||||
|
val oldTopic = passClassifier.getTopic(pass, "")
|
||||||
|
|
||||||
|
Snackbar.make(activity.window.decorView.findViewById(R.id.fam), "Pass moved to $topic", Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.undo) { passClassifier.moveToTopic(pass, oldTopic) }
|
||||||
|
.show()
|
||||||
|
passClassifier.moveToTopic(pass, topic)
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
import org.ligi.passandroid.model.PassBitmapDefinitions
|
||||||
|
import org.ligi.passandroid.model.PassStore
|
||||||
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
|
import org.ligi.passandroid.model.pass.PassField
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
import org.ligi.passandroid.model.pass.PassType
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
const val APP = "passandroid"
|
||||||
|
|
||||||
|
fun createAndAddEmptyPass(passStore: PassStore, resources: Resources): Pass {
|
||||||
|
val pass = createBasePass()
|
||||||
|
|
||||||
|
pass.description = "custom Pass"
|
||||||
|
|
||||||
|
passStore.currentPass = pass
|
||||||
|
passStore.save(pass)
|
||||||
|
|
||||||
|
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_launcher)
|
||||||
|
|
||||||
|
try {
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 90, FileOutputStream(File(passStore.getPathForID(pass.id), PassBitmapDefinitions.BITMAP_ICON + ".png")))
|
||||||
|
} catch (ignored: FileNotFoundException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPassForImageImport(resources: Resources): Pass {
|
||||||
|
return createBasePass().apply {
|
||||||
|
description = resources.getString(R.string.image_import)
|
||||||
|
|
||||||
|
fields = mutableListOf(
|
||||||
|
PassField.create(R.string.field_source, R.string.field_source_image, resources),
|
||||||
|
PassField.create(R.string.field_advice_label, R.string.field_advice_text, resources),
|
||||||
|
PassField.create(R.string.field_note, R.string.field_note_image, resources, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPassForPDFImport(resources: Resources): Pass {
|
||||||
|
return createBasePass().apply {
|
||||||
|
description = resources.getString(R.string.pdf_import)
|
||||||
|
|
||||||
|
fields = mutableListOf(
|
||||||
|
PassField.create(R.string.field_source, R.string.field_source_pdf, resources),
|
||||||
|
PassField.create(R.string.field_advice_label, R.string.field_advice_text, resources),
|
||||||
|
PassField.create(R.string.field_note, R.string.field_note_pdf, resources, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createBasePass(): PassImpl {
|
||||||
|
val pass = PassImpl(UUID.randomUUID().toString())
|
||||||
|
pass.accentColor = 0xFF0000FF.toInt()
|
||||||
|
pass.app = APP
|
||||||
|
pass.type = PassType.EVENT
|
||||||
|
return pass
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.ligi.passandroid.functions
|
||||||
|
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I got a really broken passes with invalid json from users.
|
||||||
|
* As it is not possible to change the problem in the generator side
|
||||||
|
* It has to be worked around here
|
||||||
|
*/
|
||||||
|
|
||||||
|
private val replacementMap = mapOf(
|
||||||
|
|
||||||
|
// first we try without fixing -> always positive and try to have minimal impact
|
||||||
|
"" to "",
|
||||||
|
|
||||||
|
// but here the horror starts ..
|
||||||
|
|
||||||
|
// Fix for Virgin Australia
|
||||||
|
// "value": "NTL",}
|
||||||
|
// a comma should never be before a closing curly brace like this ,}
|
||||||
|
|
||||||
|
// note the \t are greetings to Empire Theatres Tickets - without it their passes do not work
|
||||||
|
|
||||||
|
",[\n\r\t ]*\\}" to "}",
|
||||||
|
/*
|
||||||
|
Entrada cine Entradas.com
|
||||||
|
{
|
||||||
|
"key": "passSourceUpdate",
|
||||||
|
"label": "Actualiza tu entrada",
|
||||||
|
"value": "http://www.entradas.com/entradas/passbook.do?cutout
|
||||||
|
},
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
",[\n\r\t ]*\\]" to "]",
|
||||||
|
|
||||||
|
/*
|
||||||
|
forgotten value aka ( also Entradas.com):
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"latitude": ,
|
||||||
|
"longitude": ,
|
||||||
|
"relevantText": "Bienvenido a yelmo cines espacio coruña"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
":[ ]*,[\n\r\t ]*\"" to ":\"\",",
|
||||||
|
|
||||||
|
/*
|
||||||
|
from RENFE OPERADORA Billete de tren
|
||||||
|
|
||||||
|
],
|
||||||
|
"transitType": "PKTransitTypeTrain"
|
||||||
|
},
|
||||||
|
,
|
||||||
|
"relevantDate": "2013-08-10T19:15+02:00"
|
||||||
|
*/
|
||||||
|
|
||||||
|
",[\n\r\t ]*," to ","
|
||||||
|
)
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun readJSONSafely(str: String?): JSONObject? {
|
||||||
|
if (str == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var allReplaced: String = str
|
||||||
|
|
||||||
|
// first try with single fixes
|
||||||
|
for ((key, value) in replacementMap) {
|
||||||
|
try {
|
||||||
|
allReplaced = allReplaced.replace(key.toRegex(), value)
|
||||||
|
return JSONObject(str.replace(key.toRegex(), value))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
// expected because of problems in JSON we are trying to fix here
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if that did not work do combination of all - if this is not working a JSONException is thrown
|
||||||
|
return JSONObject(allReplaced)
|
||||||
|
}
|
|
@ -1,70 +0,0 @@
|
||||||
package org.ligi.passandroid.helper;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import com.google.zxing.MultiFormatWriter;
|
|
||||||
import com.google.zxing.Writer;
|
|
||||||
import com.google.zxing.WriterException;
|
|
||||||
import com.google.zxing.common.BitMatrix;
|
|
||||||
import org.ligi.passandroid.model.pass.PassBarCodeFormat;
|
|
||||||
import org.ligi.tracedroid.logging.Log;
|
|
||||||
|
|
||||||
public class BarcodeHelper {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static BitmapDrawable generateBitmapDrawable(@NonNull Resources resources, @NonNull String data, @NonNull PassBarCodeFormat type) {
|
|
||||||
final Bitmap bitmap = generateBarCodeBitmap(data, type);
|
|
||||||
if (bitmap == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final BitmapDrawable bitmapDrawable = new BitmapDrawable(resources, bitmap);
|
|
||||||
bitmapDrawable.setFilterBitmap(false);
|
|
||||||
bitmapDrawable.setAntiAlias(false);
|
|
||||||
return bitmapDrawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static Bitmap generateBarCodeBitmap(@NonNull String data, @NonNull PassBarCodeFormat type) {
|
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final BitMatrix matrix = getBitMatrix(data, type);
|
|
||||||
final boolean is1D = matrix.getHeight() == 1;
|
|
||||||
|
|
||||||
// generate an image from the byte matrix
|
|
||||||
final int width = matrix.getWidth();
|
|
||||||
final int height = is1D ? (width / 5) : matrix.getHeight();
|
|
||||||
|
|
||||||
// create buffered image to draw to
|
|
||||||
// NTFS Bitmap.Config.ALPHA_8 sounds like an awesome idea - been there - done that ..
|
|
||||||
final Bitmap barcode_image = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
|
||||||
|
|
||||||
// iterate through the matrix and draw the pixels to the image
|
|
||||||
for (int y = 0; y < height; y++) {
|
|
||||||
for (int x = 0; x < width; x++) {
|
|
||||||
barcode_image.setPixel(x, y, matrix.get(x, is1D ? 0 : y) ? 0 : 0xFFFFFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return barcode_image;
|
|
||||||
} catch (com.google.zxing.WriterException | IllegalArgumentException e) {
|
|
||||||
Log.w("could not write image " + e);
|
|
||||||
// TODO check if we should better return some rescue Image here
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BitMatrix getBitMatrix(String data, PassBarCodeFormat type) throws WriterException {
|
|
||||||
final Writer writer = new MultiFormatWriter();
|
|
||||||
return writer.encode(data, type.zxingBarCodeFormat(), 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package org.ligi.passandroid.helper
|
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.support.annotation.ColorInt
|
|
||||||
import android.support.annotation.DrawableRes
|
|
||||||
import android.support.annotation.StringRes
|
|
||||||
import org.ligi.passandroid.R
|
|
||||||
import org.ligi.passandroid.model.pass.PassType
|
|
||||||
|
|
||||||
object CategoryHelper {
|
|
||||||
|
|
||||||
@StringRes
|
|
||||||
fun getHumanCategoryString(fromPass: PassType): Int {
|
|
||||||
when (fromPass) {
|
|
||||||
PassType.BOARDING -> return R.string.boarding_pass
|
|
||||||
PassType.EVENT -> return R.string.category_event
|
|
||||||
PassType.COUPON -> return R.string.category_coupon
|
|
||||||
PassType.LOYALTY -> return R.string.category_storecard
|
|
||||||
PassType.GENERIC -> return R.string.category_generic
|
|
||||||
PassType.VOUCHER -> return R.string.categories_voucher
|
|
||||||
|
|
||||||
else -> return R.string.category_none
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
fun getCategoryDefaultBG(category: PassType): Int {
|
|
||||||
when (category) {
|
|
||||||
PassType.BOARDING -> return 0xFF3d73e9.toInt()
|
|
||||||
PassType.EVENT -> return 0xFF9f3dd0.toInt()
|
|
||||||
PassType.COUPON -> return 0xFF9ccb05.toInt()
|
|
||||||
PassType.LOYALTY -> return 0xFFf29b21.toInt()
|
|
||||||
PassType.VOUCHER -> return 0xFF2A2727.toInt()
|
|
||||||
PassType.GENERIC -> return 0xFFea3c48.toInt()
|
|
||||||
|
|
||||||
else -> return Color.WHITE
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@DrawableRes
|
|
||||||
fun getCategoryTopImageRes(type: PassType): Int {
|
|
||||||
when (type) {
|
|
||||||
PassType.BOARDING -> return R.drawable.cat_bp
|
|
||||||
PassType.EVENT -> return R.drawable.cat_et
|
|
||||||
PassType.COUPON -> return R.drawable.cat_cp
|
|
||||||
PassType.LOYALTY -> return R.drawable.cat_sc
|
|
||||||
PassType.VOUCHER -> return R.drawable.cat_ps
|
|
||||||
PassType.GENERIC -> return R.drawable.cat_none
|
|
||||||
|
|
||||||
else -> return R.drawable.cat_none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package org.ligi.passandroid.helper;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.view.View;
|
|
||||||
import org.ligi.passandroid.R;
|
|
||||||
import org.ligi.passandroid.model.PassClassifier;
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
|
|
||||||
public class MoveHelper {
|
|
||||||
public static void moveWithUndoSnackbar(final PassClassifier passClassifier, final Pass pass, String topic, final Activity activity) {
|
|
||||||
final String oldTopic = passClassifier.getTopic(pass, "");
|
|
||||||
|
|
||||||
Snackbar.make(activity.getWindow().getDecorView().findViewById(R.id.fam), "Pass moved to " + topic, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(R.string.undo, new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
passClassifier.moveToTopic(pass, oldTopic);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
passClassifier.moveToTopic(pass, topic);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package org.ligi.passandroid.helper;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import org.ligi.passandroid.R;
|
|
||||||
import org.ligi.passandroid.model.PassBitmapDefinitions;
|
|
||||||
import org.ligi.passandroid.model.PassStore;
|
|
||||||
import org.ligi.passandroid.model.pass.Pass;
|
|
||||||
import org.ligi.passandroid.model.pass.PassField;
|
|
||||||
import org.ligi.passandroid.model.pass.PassImpl;
|
|
||||||
import org.ligi.passandroid.model.pass.PassType;
|
|
||||||
|
|
||||||
public class PassTemplates {
|
|
||||||
|
|
||||||
public static final String APP = "passandroid";
|
|
||||||
|
|
||||||
public static Pass createAndAddEmptyPass(final PassStore passStore, final Resources resources) {
|
|
||||||
final PassImpl pass = new PassImpl(UUID.randomUUID().toString());
|
|
||||||
pass.setAccentColor(0xFF0000ff);
|
|
||||||
pass.setDescription("custom Pass");
|
|
||||||
pass.setApp(APP);
|
|
||||||
pass.setType(PassType.EVENT);
|
|
||||||
|
|
||||||
passStore.setCurrentPass(pass);
|
|
||||||
passStore.save(pass);
|
|
||||||
|
|
||||||
final Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_launcher);
|
|
||||||
|
|
||||||
try {
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 90, new FileOutputStream(new File(passStore.getPathForID(pass.getId()), PassBitmapDefinitions.BITMAP_ICON+".png")));
|
|
||||||
} catch (FileNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Pass createPassForImageImport() {
|
|
||||||
final PassImpl pass = new PassImpl(UUID.randomUUID().toString());
|
|
||||||
pass.setAccentColor(0xFF0000ff);
|
|
||||||
pass.setDescription("image import");
|
|
||||||
|
|
||||||
List<PassField> list = new ArrayList<>();
|
|
||||||
|
|
||||||
list.add(new PassField(null, "source", "image", false));
|
|
||||||
list.add(new PassField(null, "note", "This is imported from image - not a real pass", false));
|
|
||||||
pass.setFields(list);
|
|
||||||
pass.setApp(APP);
|
|
||||||
pass.setType(PassType.EVENT);
|
|
||||||
return pass;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package org.ligi.passandroid.helper;
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I got a really broken passes with invalid json from users.
|
|
||||||
* As it is not possible to change the problem in the generator side
|
|
||||||
* It has to be worked around here
|
|
||||||
*/
|
|
||||||
public class SafeJSONReader {
|
|
||||||
|
|
||||||
private static Map<String, String> replacementMap = new LinkedHashMap<String, String>() {{
|
|
||||||
// first we try without fixing -> always positive and try to have minimal impact
|
|
||||||
put("", "");
|
|
||||||
|
|
||||||
// but here the horror starts ..
|
|
||||||
|
|
||||||
// Fix for Virgin Australia
|
|
||||||
// "value": "NTL",}
|
|
||||||
// a comma should never be before a closing curly brace like this ,}
|
|
||||||
|
|
||||||
// note the \t are greetings to Empire Theatres Tickets - without it their passes do not work
|
|
||||||
|
|
||||||
put(",[\n\r\t ]*\\}", "}");
|
|
||||||
|
|
||||||
/*
|
|
||||||
Entrada cine Entradas.com
|
|
||||||
{
|
|
||||||
"key": "passSourceUpdate",
|
|
||||||
"label": "Actualiza tu entrada",
|
|
||||||
"value": "http://www.entradas.com/entradas/passbook.do?cutout
|
|
||||||
},
|
|
||||||
|
|
||||||
],
|
|
||||||
*/
|
|
||||||
put(",[\n\r\t ]*\\]", "]");
|
|
||||||
|
|
||||||
/*
|
|
||||||
forgotten value aka ( also Entradas.com):
|
|
||||||
"locations": [
|
|
||||||
{
|
|
||||||
"latitude": ,
|
|
||||||
"longitude": ,
|
|
||||||
"relevantText": "Bienvenido a yelmo cines espacio coruña"
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
put(":[ ]*,[\n\r\t ]*\"", ":\"\",");
|
|
||||||
|
|
||||||
/*
|
|
||||||
from RENFE OPERADORA Billete de tren
|
|
||||||
|
|
||||||
],
|
|
||||||
"transitType": "PKTransitTypeTrain"
|
|
||||||
},
|
|
||||||
,
|
|
||||||
"relevantDate": "2013-08-10T19:15+02:00"
|
|
||||||
*/
|
|
||||||
|
|
||||||
put(",[\n\r\t ]*,", ",");
|
|
||||||
|
|
||||||
}};
|
|
||||||
|
|
||||||
public static JSONObject readJSONSafely(@Nullable String str) throws JSONException {
|
|
||||||
if (str == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String allReplaced = str;
|
|
||||||
|
|
||||||
// first try with single fixes
|
|
||||||
for (Map.Entry<String, String> replacementPair : replacementMap.entrySet()) {
|
|
||||||
try {
|
|
||||||
allReplaced = allReplaced.replaceAll(replacementPair.getKey(), replacementPair.getValue());
|
|
||||||
return new JSONObject(str.replaceAll(replacementPair.getKey(), replacementPair.getValue()));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// expected because of problems in JSON we are trying to fix here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if that did not work do combination of all - if this is not working a JSONException is thrown
|
|
||||||
return new JSONObject(allReplaced);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package org.ligi.passandroid.helper;
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
public class Strings {
|
|
||||||
public static String nullToEmpty(@Nullable String in) {
|
|
||||||
return in != null ? in : "";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package org.ligi.passandroid.json_adapter;
|
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import com.squareup.moshi.FromJson;
|
|
||||||
import com.squareup.moshi.ToJson;
|
|
||||||
import org.ligi.passandroid.model.pass.PassImpl;
|
|
||||||
|
|
||||||
public class ColorAdapter {
|
|
||||||
@ToJson
|
|
||||||
String toJson(@PassImpl.HexColor int rgb) {
|
|
||||||
return String.format("#%06x", rgb);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FromJson
|
|
||||||
@PassImpl.HexColor
|
|
||||||
int fromJson(String rgb) {
|
|
||||||
return Color.parseColor(rgb);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.ligi.passandroid.json_adapter
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import com.squareup.moshi.FromJson
|
||||||
|
import com.squareup.moshi.ToJson
|
||||||
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
|
|
||||||
|
class ColorAdapter {
|
||||||
|
@ToJson
|
||||||
|
internal fun toJson(@PassImpl.HexColor rgb: Int) = String.format("#%06x", rgb)
|
||||||
|
|
||||||
|
@FromJson
|
||||||
|
@PassImpl.HexColor
|
||||||
|
internal fun fromJson(rgb: String) = Color.parseColor(rgb)
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
package org.ligi.passandroid.json_adapter;
|
|
||||||
|
|
||||||
import com.squareup.moshi.FromJson;
|
|
||||||
import com.squareup.moshi.ToJson;
|
|
||||||
import org.threeten.bp.ZonedDateTime;
|
|
||||||
import org.threeten.bp.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
public class ZonedTimeAdapter {
|
|
||||||
@ToJson
|
|
||||||
String toJson(ZonedDateTime zonedDateTime) {
|
|
||||||
return zonedDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME );
|
|
||||||
}
|
|
||||||
|
|
||||||
@FromJson
|
|
||||||
ZonedDateTime fromJson(String zonedDateTime) {
|
|
||||||
return ZonedDateTime.parse(zonedDateTime);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.ligi.passandroid.json_adapter
|
||||||
|
|
||||||
|
import com.squareup.moshi.FromJson
|
||||||
|
import com.squareup.moshi.ToJson
|
||||||
|
import org.threeten.bp.ZonedDateTime
|
||||||
|
import org.threeten.bp.format.DateTimeFormatter
|
||||||
|
|
||||||
|
class ZonedTimeAdapter {
|
||||||
|
|
||||||
|
@ToJson
|
||||||
|
internal fun toJson(zonedDateTime: ZonedDateTime) = zonedDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||||
|
|
||||||
|
@FromJson
|
||||||
|
internal fun fromJson(zonedDateTime: String) = ZonedDateTime.parse(zonedDateTime)
|
||||||
|
|
||||||
|
}
|
|
@ -3,12 +3,16 @@ package org.ligi.passandroid.model
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.squareup.moshi.JsonDataException
|
import com.squareup.moshi.JsonDataException
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import okio.Okio
|
import kotlinx.coroutines.GlobalScope
|
||||||
import org.greenrobot.eventbus.EventBus
|
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||||
import org.ligi.axt.AXT
|
import kotlinx.coroutines.launch
|
||||||
import org.ligi.passandroid.App
|
import okio.buffer
|
||||||
|
import okio.sink
|
||||||
|
import okio.source
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
import org.ligi.passandroid.BuildConfig
|
import org.ligi.passandroid.BuildConfig
|
||||||
import org.ligi.passandroid.events.PassStoreChangeEvent
|
import org.ligi.passandroid.Tracker
|
||||||
import org.ligi.passandroid.model.pass.Pass
|
import org.ligi.passandroid.model.pass.Pass
|
||||||
import org.ligi.passandroid.model.pass.PassImpl
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
import org.ligi.passandroid.reader.AppleStylePassReader
|
import org.ligi.passandroid.reader.AppleStylePassReader
|
||||||
|
@ -16,15 +20,26 @@ import org.ligi.passandroid.reader.PassReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class AndroidFileSystemPassStore(private val context: Context, settings: Settings, private val moshi: Moshi, private val bus: EventBus) : PassStore {
|
object PassStoreUpdateEvent
|
||||||
private val path: File = settings.passesDir
|
|
||||||
|
class AndroidFileSystemPassStore(
|
||||||
|
private val context: Context,
|
||||||
|
settings: Settings,
|
||||||
|
private val moshi: Moshi
|
||||||
|
) : PassStore, KoinComponent {
|
||||||
|
|
||||||
|
override val updateChannel = ConflatedBroadcastChannel<PassStoreUpdateEvent>()
|
||||||
|
|
||||||
|
private val path: File = settings.getPassesDir()
|
||||||
|
|
||||||
override val passMap = HashMap<String, Pass>()
|
override val passMap = HashMap<String, Pass>()
|
||||||
|
|
||||||
override var currentPass: Pass? = null
|
override var currentPass: Pass? = null
|
||||||
|
|
||||||
|
private val tracker: Tracker by inject()
|
||||||
|
|
||||||
override val classifier: PassClassifier by lazy {
|
override val classifier: PassClassifier by lazy {
|
||||||
val classificationFile = File(settings.stateDir, "classifier_state.json")
|
val classificationFile = File(settings.getStateDir(), "classifier_state.json")
|
||||||
FileBackedPassClassifier(classificationFile, this, moshi)
|
FileBackedPassClassifier(classificationFile, this, moshi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,11 +52,11 @@ class AndroidFileSystemPassStore(private val context: Context, settings: Setting
|
||||||
pathForID.mkdirs()
|
pathForID.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
val buffer = Okio.buffer(Okio.sink(File(pathForID, "main.json")))
|
val buffer = File(pathForID, "main.json").sink().buffer()
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
val of = com.squareup.moshi.JsonWriter.of(buffer)
|
val of = com.squareup.moshi.JsonWriter.of(buffer)
|
||||||
of.setIndent(" ")
|
of.indent = " "
|
||||||
jsonAdapter.toJson(of, pass as PassImpl)
|
jsonAdapter.toJson(of, pass as PassImpl)
|
||||||
buffer.close()
|
buffer.close()
|
||||||
of.close()
|
of.close()
|
||||||
|
@ -68,9 +83,9 @@ class AndroidFileSystemPassStore(private val context: Context, settings: Setting
|
||||||
val jsonAdapter = moshi.adapter(PassImpl::class.java)
|
val jsonAdapter = moshi.adapter(PassImpl::class.java)
|
||||||
dirty = false
|
dirty = false
|
||||||
try {
|
try {
|
||||||
result = jsonAdapter.fromJson(Okio.buffer(Okio.source(file)))
|
result = jsonAdapter.fromJson(file.source().buffer())
|
||||||
} catch (ignored: JsonDataException) {
|
} catch (ignored: JsonDataException) {
|
||||||
App.component().tracker().trackException("invalid main.json", false)
|
tracker.trackException("invalid main.json", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,14 +95,14 @@ class AndroidFileSystemPassStore(private val context: Context, settings: Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null && File(pathForID, "pass.json").exists()) {
|
if (result == null && File(pathForID, "pass.json").exists()) {
|
||||||
result = AppleStylePassReader.read(pathForID, language, context)
|
result = AppleStylePassReader.read(pathForID, language, context, tracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
save(result)
|
save(result)
|
||||||
}
|
}
|
||||||
passMap.put(id, result)
|
passMap[id] = result
|
||||||
notifyChange()
|
notifyChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +114,7 @@ class AndroidFileSystemPassStore(private val context: Context, settings: Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deletePassWithId(id: String): Boolean {
|
override fun deletePassWithId(id: String): Boolean {
|
||||||
val result = AXT.at(getPathForID(id)).deleteRecursive()
|
val result = getPathForID(id).deleteRecursively()
|
||||||
if (result) {
|
if (result) {
|
||||||
passMap.remove(id)
|
passMap.remove(id)
|
||||||
classifier.removePass(id)
|
classifier.removePass(id)
|
||||||
|
@ -113,7 +128,9 @@ class AndroidFileSystemPassStore(private val context: Context, settings: Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChange() {
|
override fun notifyChange() {
|
||||||
bus.post(PassStoreChangeEvent)
|
GlobalScope.launch {
|
||||||
|
updateChannel.send(PassStoreUpdateEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun syncPassStoreWithClassifier(defaultTopic: String) {
|
override fun syncPassStoreWithClassifier(defaultTopic: String) {
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
package org.ligi.passandroid.model;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
|
||||||
import java.io.File;
|
|
||||||
import org.ligi.passandroid.R;
|
|
||||||
import org.ligi.passandroid.model.comparator.PassSortOrder;
|
|
||||||
import static org.ligi.passandroid.R.string.preference_key_autolight;
|
|
||||||
import static org.ligi.passandroid.R.string.preference_key_condensed;
|
|
||||||
|
|
||||||
public class AndroidSettings implements Settings {
|
|
||||||
public final Context context;
|
|
||||||
|
|
||||||
final SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
public AndroidSettings(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PassSortOrder getSortOrder() {
|
|
||||||
final String key = context.getString(R.string.preference_key_sort);
|
|
||||||
final String stringValue = sharedPreferences.getString(key, "0");
|
|
||||||
final int id = Integer.valueOf(stringValue);
|
|
||||||
for (PassSortOrder order : PassSortOrder.values()) {
|
|
||||||
if (order.getInt() == id) {
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PassSortOrder.DATE_ASC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doTraceDroidEmailSend() {
|
|
||||||
// will be overridden in test-module
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getPassesDir() {
|
|
||||||
return new File(context.getFilesDir().getAbsolutePath(), "passes");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getStateDir() {
|
|
||||||
return new File(context.getFilesDir(), "state");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCondensedModeEnabled() {
|
|
||||||
return sharedPreferences.getBoolean(context.getString(preference_key_condensed), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAutomaticLightEnabled() {
|
|
||||||
return sharedPreferences.getBoolean(context.getString(preference_key_autolight), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNightMode() {
|
|
||||||
final String key = sharedPreferences.getString(context.getString(R.string.preference_key_nightmode), "auto");
|
|
||||||
switch (key) {
|
|
||||||
case "day":
|
|
||||||
return AppCompatDelegate.MODE_NIGHT_NO;
|
|
||||||
case "night":
|
|
||||||
return AppCompatDelegate.MODE_NIGHT_YES;
|
|
||||||
case "auto":
|
|
||||||
return AppCompatDelegate.MODE_NIGHT_AUTO;
|
|
||||||
|
|
||||||
}
|
|
||||||
return AppCompatDelegate.MODE_NIGHT_AUTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.ligi.passandroid.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import org.ligi.passandroid.R
|
||||||
|
import org.ligi.passandroid.R.string.preference_key_autolight
|
||||||
|
import org.ligi.passandroid.R.string.preference_key_condensed
|
||||||
|
import org.ligi.passandroid.model.comparator.PassSortOrder
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class AndroidSettings(val context: Context) : Settings {
|
||||||
|
|
||||||
|
private val sharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) }
|
||||||
|
|
||||||
|
override fun getSortOrder(): PassSortOrder {
|
||||||
|
val key = context.getString(R.string.preference_key_sort)
|
||||||
|
val stringValue = sharedPreferences.getString(key, "0")
|
||||||
|
val id = Integer.valueOf(stringValue!!)
|
||||||
|
|
||||||
|
return PassSortOrder.values().first { it.int == id }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doTraceDroidEmailSend() = true
|
||||||
|
|
||||||
|
override fun getPassesDir() = File(context.filesDir.absolutePath, "passes")
|
||||||
|
|
||||||
|
override fun getStateDir() = File(context.filesDir, "state")
|
||||||
|
|
||||||
|
override fun isCondensedModeEnabled() = sharedPreferences.getBoolean(context.getString(preference_key_condensed), false)
|
||||||
|
|
||||||
|
override fun isAutomaticLightEnabled() = sharedPreferences.getBoolean(context.getString(preference_key_autolight), true)
|
||||||
|
|
||||||
|
override fun getNightMode(): Int {
|
||||||
|
val key = sharedPreferences.getString(context.getString(R.string.preference_key_nightmode), "auto")
|
||||||
|
return when (key) {
|
||||||
|
"day" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
|
"night" -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
"auto" -> AppCompatDelegate.MODE_NIGHT_AUTO
|
||||||
|
else -> AppCompatDelegate.MODE_NIGHT_AUTO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package org.ligi.passandroid.model
|
package org.ligi.passandroid.model
|
||||||
|
|
||||||
import org.ligi.passandroid.Tracker
|
import org.ligi.passandroid.Tracker
|
||||||
import org.ligi.passandroid.helper.Strings
|
|
||||||
import org.ligi.passandroid.model.pass.PassField
|
import org.ligi.passandroid.model.pass.PassField
|
||||||
import org.ligi.passandroid.model.pass.PassImpl
|
import org.ligi.passandroid.model.pass.PassImpl
|
||||||
import org.threeten.bp.DateTimeException
|
import org.threeten.bp.DateTimeException
|
||||||
|
@ -25,10 +24,10 @@ class ApplePassbookQuirkCorrector(val tracker: Tracker) {
|
||||||
tryToFindDate(pass)
|
tryToFindDate(pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryToFindDate(pass: PassImpl) {
|
private fun tryToFindDate(pass: PassImpl) {
|
||||||
|
|
||||||
if (pass.calendarTimespan == null) {
|
if (pass.calendarTimespan == null) {
|
||||||
val foundDate = pass.fields.filter { it.key.equals("date") }.map {
|
val foundDate = pass.fields.filter { "date" == it.key }.map {
|
||||||
try {
|
try {
|
||||||
ZonedDateTime.parse(it.value)
|
ZonedDateTime.parse(it.value)
|
||||||
} catch (e: DateTimeException) {
|
} catch (e: DateTimeException) {
|
||||||
|
@ -45,11 +44,11 @@ class ApplePassbookQuirkCorrector(val tracker: Tracker) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPassFieldForKey(pass: PassImpl, key: String): PassField? {
|
private fun getPassFieldForKey(pass: PassImpl, key: String): PassField? {
|
||||||
return pass.fields.firstOrNull() { it.key != null && it.key == key }
|
return pass.fields.firstOrNull { it.key != null && it.key == key }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPassFieldThatMatchesLabel(pass: PassImpl, matcher: String): PassField? {
|
private fun getPassFieldThatMatchesLabel(pass: PassImpl, matcher: String): PassField? {
|
||||||
return pass.fields.firstOrNull() {
|
return pass.fields.firstOrNull {
|
||||||
val label = it.label
|
val label = it.label
|
||||||
label != null && label.matches(matcher.toRegex())
|
label != null && label.matches(matcher.toRegex())
|
||||||
}
|
}
|
||||||
|
@ -152,7 +151,7 @@ class ApplePassbookQuirkCorrector(val tracker: Tracker) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun careForWestbahn(pass: PassImpl) {
|
private fun careForWestbahn(pass: PassImpl) {
|
||||||
if (pass.calendarTimespan != null || Strings.nullToEmpty(pass.creator) != "WESTbahn") {
|
if (pass.calendarTimespan != null || (pass.creator ?: "") != "WESTbahn") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
package org.ligi.passandroid.model;
|
package org.ligi.passandroid.model;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.ibm.icu.text.CharsetDetector;
|
import com.ibm.icu.text.CharsetDetector;
|
||||||
import com.ibm.icu.text.CharsetMatch;
|
import com.ibm.icu.text.CharsetMatch;
|
||||||
|
|
||||||
import org.ligi.passandroid.App;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public class AppleStylePassTranslation extends HashMap<String, String> {
|
public class AppleStylePassTranslation extends HashMap<String, String> {
|
||||||
|
|
||||||
public String translate(String key) {
|
public String translate(String key) {
|
||||||
|
@ -63,6 +58,13 @@ public class AppleStylePassTranslation extends HashMap<String, String> {
|
||||||
dataInputStream.readFully(fileData);
|
dataInputStream.readFully(fileData);
|
||||||
dataInputStream.close();
|
dataInputStream.close();
|
||||||
|
|
||||||
|
if (fileData[0] == (byte) 0xEF && fileData[1] == (byte) 0xBB && fileData[2] == (byte) 0xBF) {
|
||||||
|
final byte[] crop = new byte[fileData.length - 3];
|
||||||
|
System.arraycopy(fileData, 3, crop, 0, crop.length);
|
||||||
|
//noinspection CharsetObjectCanBeUsed
|
||||||
|
return new String(crop, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
final CharsetMatch match = new CharsetDetector().setText(fileData).detect();
|
final CharsetMatch match = new CharsetDetector().setText(fileData).detect();
|
||||||
|
|
||||||
if (match != null) try {
|
if (match != null) try {
|
||||||
|
@ -71,7 +73,6 @@ public class AppleStylePassTranslation extends HashMap<String, String> {
|
||||||
}
|
}
|
||||||
return new String(fileData);
|
return new String(fileData);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
App.component().tracker().trackException("problem_reading_translation", e, false);
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.ligi.passandroid.model;
|
package org.ligi.passandroid.model;
|
||||||
|
|
||||||
public class PassBitmapDefinitions {
|
public class PassBitmapDefinitions {
|
||||||
|
|
||||||
public final static String BITMAP_ICON = "icon";
|
public final static String BITMAP_ICON = "icon";
|
||||||
public final static String BITMAP_THUMBNAIL = "thumbnail";
|
public final static String BITMAP_THUMBNAIL = "thumbnail";
|
||||||
public final static String BITMAP_STRIP = "strip";
|
public final static String BITMAP_STRIP = "strip";
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue