mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 15:47:53 -05:00
Compare commits
350 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a408f8d727 | ||
|
|
2d7bb02d1a | ||
|
|
38c8e38ed6 | ||
|
|
28b95b8f75 | ||
|
|
c1ebbdb997 | ||
|
|
fadba7a11c | ||
|
|
fd61434565 | ||
|
|
398dff4b3c | ||
|
|
5c95b750b2 | ||
|
|
9851c0a2fa | ||
|
|
c121f846c5 | ||
|
|
c20ba027cf | ||
|
|
e5dd26b8ee | ||
|
|
3b449464ac | ||
|
|
3f4d4e38cd | ||
|
|
30ccd03686 | ||
|
|
5493947c28 | ||
|
|
4172903b42 | ||
|
|
00b1368176 | ||
|
|
09fee5628f | ||
|
|
7a7a2f8361 | ||
|
|
a9ced56023 | ||
|
|
a4af171598 | ||
|
|
164c82a779 | ||
|
|
608c3ab863 | ||
|
|
379a71c7ad | ||
|
|
ee6a6dffcf | ||
|
|
1d6a393914 | ||
|
|
5c4a905ac0 | ||
|
|
91ce71ea68 | ||
|
|
f07ac3e026 | ||
|
|
649f2c47b4 | ||
|
|
62dcc373ed | ||
|
|
e9b04adec6 | ||
|
|
636be16bdd | ||
|
|
5ad28f37b8 | ||
|
|
5ff6059e86 | ||
|
|
b9df712394 | ||
|
|
790fd7e48f | ||
|
|
bb43266a01 | ||
|
|
450cfce84a | ||
|
|
6cef56b38b | ||
|
|
fbb7cf7e9c | ||
|
|
9ab2a6a5b2 | ||
|
|
682fc8303c | ||
|
|
aa1274566b | ||
|
|
d8cd581cb0 | ||
|
|
2cd00f9103 | ||
|
|
ae07f94b25 | ||
|
|
22d671263a | ||
|
|
227f30361f | ||
|
|
b964652b83 | ||
|
|
f926ffa1d0 | ||
|
|
d03b8b5635 | ||
|
|
4e5fea7a52 | ||
|
|
95cb6c0a08 | ||
|
|
d350d0b2c7 | ||
|
|
ac0f6f6f3e | ||
|
|
ab030ba002 | ||
|
|
93103c8c6d | ||
|
|
576ec1e6de | ||
|
|
b602ce5d78 | ||
|
|
0ecd38ed1c | ||
|
|
d48e02463c | ||
|
|
b9d9c8d2e3 | ||
|
|
3ae665b70f | ||
|
|
9cf9959b6b | ||
|
|
d11e2c166b | ||
|
|
f783be7a4f | ||
|
|
9ee96b88e8 | ||
|
|
ba896fc1db | ||
|
|
47f1ea80b6 | ||
|
|
b5efa28e85 | ||
|
|
d456a8920d | ||
|
|
4ef0c96b29 | ||
|
|
343c77f9e3 | ||
|
|
1425d4af58 | ||
|
|
48510494eb | ||
|
|
d5d53b241a | ||
|
|
901c2d8154 | ||
|
|
84d7e15b5c | ||
|
|
b8fa4d7060 | ||
|
|
da9e3bb6b2 | ||
|
|
3a5973a04d | ||
|
|
673e64924b | ||
|
|
5f99f2b17e | ||
|
|
bf05103955 | ||
|
|
5ea6155c39 | ||
|
|
abb1cd29f0 | ||
|
|
52363cdff4 | ||
|
|
ac4f4e3a9a | ||
|
|
555387e20d | ||
|
|
70ae8ff167 | ||
|
|
86512532f1 | ||
|
|
ebf6318aa2 | ||
|
|
fd482a4cba | ||
|
|
54d91dc8a1 | ||
|
|
9fa7fe388f | ||
|
|
1415d8da3e | ||
|
|
0ad5de18e1 | ||
|
|
9659a2a2cd | ||
|
|
1dd894bd27 | ||
|
|
0d9151294e | ||
|
|
09dda99afc | ||
|
|
a4fb91b9aa | ||
|
|
036b821b28 | ||
|
|
479a35657f | ||
|
|
eca9d1c74c | ||
|
|
95055f1ce6 | ||
|
|
f4a5ae74d6 | ||
|
|
16952186c7 | ||
|
|
7d12279b0d | ||
|
|
15a49344a3 | ||
|
|
ff15ce0615 | ||
|
|
57144c4cd1 | ||
|
|
50387f55d4 | ||
|
|
0fa07d3d0b | ||
|
|
05516f88fc | ||
|
|
841a5e3ddb | ||
|
|
2889b45c51 | ||
|
|
9ebb53b224 | ||
|
|
14b8b3bd7a | ||
|
|
3e4c3e9852 | ||
|
|
fa417cdfe5 | ||
|
|
a9863d1921 | ||
|
|
f2b7a63a02 | ||
|
|
8e0a40ee3f | ||
|
|
21064803eb | ||
|
|
031b751a9d | ||
|
|
bb9b640aa5 | ||
|
|
1681d077b6 | ||
|
|
3500444e0c | ||
|
|
e05ebbe607 | ||
|
|
b3bb20c795 | ||
|
|
b0315f279a | ||
|
|
9e788cb604 | ||
|
|
090f3dc80b | ||
|
|
e4f2f8f46b | ||
|
|
e5ecd59569 | ||
|
|
441ae9587e | ||
|
|
0c1e16592d | ||
|
|
02659f840e | ||
|
|
eca5ec10d3 | ||
|
|
dbc8df018d | ||
|
|
874b887da1 | ||
|
|
5367324219 | ||
|
|
6df58e0574 | ||
|
|
c8b6328cc6 | ||
|
|
90616214f0 | ||
|
|
e3bce7eb50 | ||
|
|
b2d1660cf8 | ||
|
|
3de24bb6f0 | ||
|
|
b4e403d214 | ||
|
|
b66351ab58 | ||
|
|
c9d775f426 | ||
|
|
6d4af3d86b | ||
|
|
37803bd906 | ||
|
|
f3fcea7ab1 | ||
|
|
9a67ea039c | ||
|
|
bf94d208bd | ||
|
|
28c0b488e6 | ||
|
|
dc926bbfe7 | ||
|
|
00d5fa4c2d | ||
|
|
9aca91fb86 | ||
|
|
bdab8624b5 | ||
|
|
9bdfe64c79 | ||
|
|
f5f7e40219 | ||
|
|
2f51e7db68 | ||
|
|
2cb9846f42 | ||
|
|
1dbe46b97e | ||
|
|
7e5611b062 | ||
|
|
d3a7acd9ab | ||
|
|
c023a9787d | ||
|
|
6de0ed6956 | ||
|
|
aa5f0a7bf8 | ||
|
|
6dd5bd957c | ||
|
|
c69df5edfd | ||
|
|
758d034c31 | ||
|
|
7bcf82a619 | ||
|
|
fd755185b9 | ||
|
|
0ac57be5f1 | ||
|
|
bb929a48ea | ||
|
|
218850be33 | ||
|
|
ec07cceced | ||
|
|
71ac1c5dad | ||
|
|
b16ecfbb2c | ||
|
|
c64bfbabc0 | ||
|
|
fc8073f717 | ||
|
|
7f56617a3c | ||
|
|
f5dc2930b5 | ||
|
|
4bdc0fef70 | ||
|
|
2f3c96b97c | ||
|
|
6ff11e22e1 | ||
|
|
0b188f2ba7 | ||
|
|
827d5ba3f9 | ||
|
|
05fea6e208 | ||
|
|
6064d29cb0 | ||
|
|
0a92daa77a | ||
|
|
bb065aead4 | ||
|
|
56467c2680 | ||
|
|
1941db1e52 | ||
|
|
c595981996 | ||
|
|
0145a820de | ||
|
|
224ab7f2e7 | ||
|
|
1b47a62c70 | ||
|
|
f241bad3e4 | ||
|
|
c8657870af | ||
|
|
0ed7a0a6e1 | ||
|
|
325330c542 | ||
|
|
2506f60348 | ||
|
|
5bf6f52425 | ||
|
|
fb1728e5de | ||
|
|
d321f733c1 | ||
|
|
8ff46f8754 | ||
|
|
a061978c93 | ||
|
|
f3bfc9b278 | ||
|
|
43ebe5e029 | ||
|
|
2357f4d430 | ||
|
|
7869feb3a1 | ||
|
|
53ce856c33 | ||
|
|
aa321a71ad | ||
|
|
f721237fbc | ||
|
|
194a7ae365 | ||
|
|
1aa1d8bcc1 | ||
|
|
a8355f609b | ||
|
|
7c766d0832 | ||
|
|
3abe99470e | ||
|
|
6937342226 | ||
|
|
e229aa9565 | ||
|
|
5fd629bdbc | ||
|
|
4c261e1e04 | ||
|
|
c8be038b81 | ||
|
|
268b4cf827 | ||
|
|
ada8fd9bd4 | ||
|
|
285c3d6a38 | ||
|
|
07efe99630 | ||
|
|
a7d3a1c30d | ||
|
|
fcc995397f | ||
|
|
cef0d037f7 | ||
|
|
6e390717da | ||
|
|
f785586c71 | ||
|
|
e66c5e36fb | ||
|
|
fd0ca37a6a | ||
|
|
e926a397a7 | ||
|
|
4f9a6b307f | ||
|
|
df60f6adc2 | ||
|
|
d6287a3cf1 | ||
|
|
7f81d727f0 | ||
|
|
03239f0cab | ||
|
|
4b6497cba2 | ||
|
|
99008291fb | ||
|
|
079049722c | ||
|
|
73200e1ac3 | ||
|
|
39b816ddb1 | ||
|
|
be43c1633d | ||
|
|
f6c9613731 | ||
|
|
99de8cffd8 | ||
|
|
a867265c66 | ||
|
|
f43df37c80 | ||
|
|
cb70b1229f | ||
|
|
b0b54a1065 | ||
|
|
4ccfa7fb84 | ||
|
|
9932b10652 | ||
|
|
60fe6a13ff | ||
|
|
0bf30f08a1 | ||
|
|
112fca041b | ||
|
|
59af5cb07b | ||
|
|
9182837ae8 | ||
|
|
3f3d85c8b6 | ||
|
|
29c068df47 | ||
|
|
03a480f3b9 | ||
|
|
0feb0fcd16 | ||
|
|
7f1a221fec | ||
|
|
7b52338669 | ||
|
|
0a2925eafa | ||
|
|
92f37376a6 | ||
|
|
08fbc30a29 | ||
|
|
91dbcf339f | ||
|
|
24fe6886c9 | ||
|
|
270546d022 | ||
|
|
fe278324f3 | ||
|
|
c7c18a0a14 | ||
|
|
d6fcb71722 | ||
|
|
32ce1e02aa | ||
|
|
8a8faac201 | ||
|
|
2793f38d19 | ||
|
|
654e3f7843 | ||
|
|
2a6fe92b1b | ||
|
|
b3cd102332 | ||
|
|
e12bfa81dc | ||
|
|
e859e7849d | ||
|
|
7a4c9ce84f | ||
|
|
6419ca7d11 | ||
|
|
d1b23c712a | ||
|
|
ef5b525401 | ||
|
|
a8d5cb9ef8 | ||
|
|
42090310d4 | ||
|
|
9d8da2a451 | ||
|
|
fb488af2b7 | ||
|
|
71052c9f69 | ||
|
|
2a285f6d21 | ||
|
|
e668ae7d9d | ||
|
|
4a197c542a | ||
|
|
720c6b6510 | ||
|
|
d932ebb9f2 | ||
|
|
77b6d8724b | ||
|
|
5ff002da51 | ||
|
|
16b3fbdf9c | ||
|
|
4f11eceb3b | ||
|
|
c11d777bcc | ||
|
|
49cda90ac6 | ||
|
|
63e4f4fd0d | ||
|
|
c44845582c | ||
|
|
b0771d3b01 | ||
|
|
285a7a9aa7 | ||
|
|
818c5e1643 | ||
|
|
7ffaf65635 | ||
|
|
bf50dcbaf3 | ||
|
|
3e83035cf6 | ||
|
|
e921aab322 | ||
|
|
1ea125d7f9 | ||
|
|
c828ec516c | ||
|
|
5704ce1381 | ||
|
|
ca33184e81 | ||
|
|
82db76f3a9 | ||
|
|
ab6955cf9b | ||
|
|
a1c81c3223 | ||
|
|
43e7114f16 | ||
|
|
d1b84097f6 | ||
|
|
2c9b3896c9 | ||
|
|
3dbee69847 | ||
|
|
4f9414ab4f | ||
|
|
5a4043bf90 | ||
|
|
f742639925 | ||
|
|
e50cca781e | ||
|
|
1abff7c900 | ||
|
|
8f24fcee96 | ||
|
|
f3e1bfc503 | ||
|
|
65e3b87df4 | ||
|
|
2dbe9ebb8a | ||
|
|
ccfbe1f679 | ||
|
|
3a4467db83 | ||
|
|
419d28163e | ||
|
|
89483c4c4e | ||
|
|
4e921eb5d7 | ||
|
|
0cf266c6f1 | ||
|
|
20f9f971a1 | ||
|
|
0150ca81cf | ||
|
|
4fd6092f8d | ||
|
|
a488003e4a |
15
.github/workflows/android.yml
vendored
15
.github/workflows/android.yml
vendored
@@ -10,6 +10,9 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -20,17 +23,17 @@ jobs:
|
||||
- name: Fail on bad translations
|
||||
run: if grep -ri "<xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: set up OpenJDK 17
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y openjdk-17-jdk-headless
|
||||
sudo update-alternatives --auto java
|
||||
- name: Build
|
||||
run: ./gradlew assembleRelease
|
||||
- name: Check lint
|
||||
run: ./gradlew lintRelease
|
||||
- name: Run unit tests
|
||||
run: ./gradlew testReleaseUnitTest || ./gradlew testReleaseUnitTest
|
||||
run: timeout 5m ./gradlew testReleaseUnitTest || { ./gradlew --stop && timeout 5m ./gradlew testReleaseUnitTest; }
|
||||
- name: SpotBugs
|
||||
run: ./gradlew spotbugsRelease
|
||||
- name: Archive test results
|
||||
|
||||
6
.github/workflows/autoclose-needs-info.yml
vendored
6
.github/workflows/autoclose-needs-info.yml
vendored
@@ -13,12 +13,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
days-before-stale: -1
|
||||
days-before-close: 90
|
||||
stale-issue-message: ""
|
||||
stale-pr-message: ""
|
||||
close-issue-message: 'This issue is missing necessary information and cannot be worked on in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
|
||||
close-pr-message: 'This PR is missing necessary information and cannot be merged in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
|
||||
only-labels: 'state: needs info'
|
||||
stale-issue-label: 'state: needs info'
|
||||
stale-pr-label: 'state: needs info'
|
||||
remove-stale-when-updated: false
|
||||
enable-statistics: true
|
||||
|
||||
44
.scripts/dump_stocard_stores.py
Executable file
44
.scripts/dump_stocard_stores.py
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import csv
|
||||
import json
|
||||
import msgpack
|
||||
|
||||
MSGPACK = "bootstrapdata.msgpack"
|
||||
OUTFILE = "stocard_stores.csv"
|
||||
|
||||
|
||||
def load(fh):
|
||||
data = []
|
||||
for r in msgpack.Unpacker(fh, raw=False):
|
||||
if r["collection"] == "/loyalty-card-providers/":
|
||||
d = json.loads(r["data"])
|
||||
data.append([r["resource_id"], d["name"], d["default_barcode_format"]])
|
||||
return data
|
||||
|
||||
|
||||
def save(data, output_file=OUTFILE):
|
||||
with open(output_file, "w") as fh:
|
||||
writer = csv.writer(fh, lineterminator="\n")
|
||||
writer.writerow(["_id", "name", "barcodeFormat"])
|
||||
for row in data:
|
||||
writer.writerow(row)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
epilog=f"INPUT_FILE must be a .msgpack or .apk and defaults to {MSGPACK}; "
|
||||
f"OUTPUT_FILE defaults to {OUTFILE}")
|
||||
parser.add_argument("input_file", metavar="INPUT_FILE", nargs="?", default=MSGPACK)
|
||||
parser.add_argument("output_file", metavar="OUTPUT_FILE", nargs="?", default=OUTFILE)
|
||||
args = parser.parse_args()
|
||||
if args.input_file.lower().endswith(".apk"):
|
||||
import zipfile
|
||||
with zipfile.ZipFile(args.input_file) as zf:
|
||||
with zf.open(f"assets/{MSGPACK}") as fh:
|
||||
data = load(fh)
|
||||
else:
|
||||
with open(args.input_file, "rb") as fh:
|
||||
data = load(fh)
|
||||
save(data, args.output_file)
|
||||
66
CHANGELOG.md
66
CHANGELOG.md
@@ -1,58 +1,98 @@
|
||||
# Changelog
|
||||
|
||||
## v2.23.1 - 121
|
||||
## v2.25.2 - 129 (2023-07-27)
|
||||
|
||||
- Improved Catima importer (fixes cards missing when importing)
|
||||
- Fix crash when rotating screen while setting valid from/expiry date
|
||||
- Minor UI tweaks
|
||||
|
||||
## v2.25.1 - 128 (2023-07-17)
|
||||
|
||||
- Fix rare crash
|
||||
|
||||
## v2.25.0 - 127 (2023-07-09)
|
||||
|
||||
- Barcode rendering improvements
|
||||
- Basic interoperability with external apps (Android 6.0+)
|
||||
- Reorganized settings screen
|
||||
- Fix importing from some browsers that add a trailing / to the share URL
|
||||
|
||||
## v2.24.2 - 126 (2023-06-18)
|
||||
|
||||
- Various RTL fixes
|
||||
|
||||
## v2.24.1 - 125 (2023-06-11)
|
||||
|
||||
- Deal more gracefully with missing header colours
|
||||
|
||||
## v2.24.0 - 124 (2023-06-10)
|
||||
|
||||
- Support selecting exactly which details to view in card overview
|
||||
|
||||
## v2.23.3 - 123 (2023-06-03)
|
||||
|
||||
- Minor UI improvements
|
||||
- Fix new design not being usable on devices with square screens
|
||||
|
||||
## v2.23.2 - 122 (2023-05-30)
|
||||
|
||||
- Long-press card icon in view activity to change it
|
||||
- Improve button styling in Groups screen
|
||||
- Fix long barcode values causing barcode to scale down to nothing
|
||||
|
||||
## v2.23.1 - 121 (2023-05-27)
|
||||
|
||||
- Update used libraries
|
||||
|
||||
## v2.23.0 - 120
|
||||
## v2.23.0 - 120 (2023-05-25)
|
||||
|
||||
- Complete redesign of main and loyalty card view screens
|
||||
- Material You design for the settings screen
|
||||
- Fix crash when using "Take a photo" with disabled camera app
|
||||
|
||||
## v2.22.1 - 119
|
||||
## v2.22.1 - 119 (2023-04-14)
|
||||
|
||||
- Use Material You colours on more devices (Google library update)
|
||||
|
||||
## v2.22.0 - 118
|
||||
## v2.22.0 - 118 (2023-03-18)
|
||||
|
||||
- Support setting start of card validity
|
||||
- Fix Stocard import (Stocard's export format changed)
|
||||
|
||||
## v2.21.2 - 117
|
||||
## v2.21.2 - 117 (2023-01-27)
|
||||
|
||||
- Remove unnecessary permissions
|
||||
- Target Android 13
|
||||
|
||||
## v2.21.1 - 116
|
||||
## v2.21.1 - 116 (2022-12-06)
|
||||
|
||||
- Fix quick spend dialog not allowing , separator
|
||||
- Support loading image from file manager
|
||||
|
||||
## v2.21.0 - 115
|
||||
## v2.21.0 - 115 (2022-11-06)
|
||||
|
||||
- Open image in gallery on long-press
|
||||
- Apply Material style to dialogs
|
||||
- Support creating card by sharing an image to Catima
|
||||
- Add quick spend button to card screen
|
||||
|
||||
## v2.20.0 - 114
|
||||
## v2.20.0 - 114 (2022-09-21)
|
||||
|
||||
- Add Monochrome icon for Android 13
|
||||
- Improve first launch screen
|
||||
- Fidme import fixes
|
||||
|
||||
## v2.19.0 - 113
|
||||
## v2.19.0 - 113 (2022-08-14)
|
||||
|
||||
- Add previous and next buttons to the loyalty card view
|
||||
- Fix foreground colour on edit button
|
||||
- Replace floppy disk save icon with checkmark
|
||||
|
||||
## v2.18.2 - 112
|
||||
## v2.18.2 - 112 (2022-07-29)
|
||||
|
||||
- Make the possibility to set a custom header more visible
|
||||
|
||||
## v2.18.1 - 111
|
||||
## v2.18.1 - 111 (2022-07-24)
|
||||
|
||||
- Arabic language support
|
||||
- Display archived card count in group overview
|
||||
@@ -62,11 +102,11 @@
|
||||
- Fix crash when leaving cardview in RTL layouts for cards with expiry or balance
|
||||
- Fix back arrow in card view pointing the wrong way in RTL layouts
|
||||
|
||||
## v2.17.1 - 109
|
||||
## v2.17.1 - 109 (2022-06-28)
|
||||
|
||||
- Fix incorrect text colour on "No barcode" button
|
||||
|
||||
## v2.17.0 - 108
|
||||
## v2.17.0 - 108 (2022-06-24)
|
||||
|
||||
- Add card duplication feature
|
||||
- Don't allow choosing expiry before 1970 (they never worked anyway)
|
||||
|
||||
40
Gemfile.lock
40
Gemfile.lock
@@ -8,20 +8,20 @@ GEM
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.749.0)
|
||||
aws-sdk-core (3.171.0)
|
||||
aws-partitions (1.793.0)
|
||||
aws-sdk-core (3.180.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.63.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (1.71.0)
|
||||
aws-sdk-core (~> 3, >= 3.177.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.120.1)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-s3 (1.132.0)
|
||||
aws-sdk-core (~> 3, >= 3.179.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-sigv4 (~> 1.6)
|
||||
aws-sigv4 (1.6.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
@@ -30,13 +30,13 @@ GEM
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
digest-crc (0.6.5)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.99.0)
|
||||
excon (0.100.0)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
@@ -65,8 +65,8 @@ GEM
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.212.2)
|
||||
fastimage (2.2.7)
|
||||
fastlane (2.214.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@@ -90,7 +90,7 @@ GEM
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
@@ -106,9 +106,9 @@ GEM
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.39.0)
|
||||
google-apis-androidpublisher_v3 (0.46.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.0)
|
||||
google-apis-core (0.11.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
@@ -137,7 +137,7 @@ GEM
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.5.2)
|
||||
googleauth (1.7.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
@@ -150,25 +150,25 @@ GEM
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
jwt (2.7.0)
|
||||
jwt (2.7.1)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
multipart-post (2.3.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.7.0)
|
||||
public_suffix (5.0.1)
|
||||
public_suffix (5.0.3)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rexml (3.2.6)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
|
||||
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
Catima is designed to use as little permissions as possible to limit both the attack surface as well as the damage that can be done when abusing a security flaw.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Only the most recent stable release is supported.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Security vulnerabilities can be reported through [GitHub Security Advisories](https://github.com/CatimaLoyalty/Android/security/advisories) or [the contact info written on my personal website](https://sylviavanos.nl/#contact). Currently, Matrix is the only end-to-end encrypted option.
|
||||
|
||||
Please note that only security vulnerabilities in Catima should be reported as stated above. For other issues, including antivirus false positives and malicious applications trying to trick people into granting them Catima's "Read Cards" permission, please use [regular issues](https://github.com/CatimaLoyalty/Android/issues).
|
||||
@@ -19,8 +19,8 @@ android {
|
||||
applicationId "me.hackerchick.catima"
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
versionCode 121
|
||||
versionName "2.23.1"
|
||||
versionCode 129
|
||||
versionName "2.25.2"
|
||||
|
||||
vectorDrawables.useSupportLibrary true
|
||||
multiDexEnabled true
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<permission
|
||||
android:description="@string/permissionReadCardsDescription"
|
||||
android:icon="@drawable/ic_launcher_foreground"
|
||||
android:label="@string/permissionReadCardsLabel"
|
||||
android:name="${applicationId}.READ_CARDS"
|
||||
android:protectionLevel="dangerous" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
@@ -155,6 +162,12 @@
|
||||
android:name=".UCropWrapper"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
|
||||
<provider
|
||||
android:name=".contentprovider.CardsContentProvider"
|
||||
android:authorities="${applicationId}.contentprovider.cards"
|
||||
android:exported="true"
|
||||
android:readPermission="${applicationId}.READ_CARDS"/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}"
|
||||
|
||||
@@ -94,10 +94,12 @@ public class AboutContent {
|
||||
|
||||
public String getContributorInfo() {
|
||||
StringBuilder contributorInfo = new StringBuilder();
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_contributors), getContributors()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
contributorInfo.append(getCopyright());
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(context.getString(R.string.app_copyright_old));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_contributors), getContributors()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_libraries), getThirdPartyLibraries()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
contributorInfo.append("\n\n");
|
||||
contributorInfo.append(HtmlCompat.fromHtml(String.format(context.getString(R.string.app_resources), getUsedThirdPartyAssets()), HtmlCompat.FROM_HTML_MODE_COMPACT));
|
||||
|
||||
@@ -41,6 +41,8 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
private final CatimaBarcode format;
|
||||
private final int imageHeight;
|
||||
private final int imageWidth;
|
||||
private final int imagePadding;
|
||||
private final boolean widthPadding;
|
||||
private final boolean showFallback;
|
||||
private final BarcodeImageWriterResultCallback callback;
|
||||
|
||||
@@ -61,32 +63,39 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
cardId = cardIdString;
|
||||
format = barcodeFormat;
|
||||
|
||||
int padding = 0;
|
||||
int imageViewHeight = imageView.getHeight();
|
||||
int imageViewWidth = imageView.getWidth();
|
||||
|
||||
// Some barcodes already have internal whitespace and shouldn't get extra padding
|
||||
// TODO: Get rid of this hack by somehow detecting this extra whitespace
|
||||
if (roundCornerPadding && !barcodeFormat.hasInternalPadding()) {
|
||||
padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
|
||||
imagePadding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
|
||||
} else {
|
||||
imagePadding = 0;
|
||||
}
|
||||
|
||||
if (format.isSquare() && imageViewWidth > imageViewHeight) {
|
||||
imageViewWidth -= imagePadding;
|
||||
widthPadding = true;
|
||||
} else {
|
||||
imageViewHeight -= imagePadding;
|
||||
widthPadding = false;
|
||||
}
|
||||
|
||||
final int MAX_WIDTH = getMaxWidth(format);
|
||||
|
||||
int tempImageHeight;
|
||||
int tempImageWidth;
|
||||
|
||||
if (imageView.getWidth() < MAX_WIDTH) {
|
||||
tempImageHeight = imageView.getHeight();
|
||||
tempImageWidth = imageView.getWidth();
|
||||
if (format.isSquare()) {
|
||||
imageHeight = imageWidth = Math.min(imageViewHeight, Math.min(MAX_WIDTH, imageViewWidth));
|
||||
} else if (imageView.getWidth() < MAX_WIDTH) {
|
||||
imageHeight = imageViewHeight;
|
||||
imageWidth = imageViewWidth;
|
||||
} else {
|
||||
// Scale down the image to reduce the memory needed to produce it
|
||||
tempImageWidth = MAX_WIDTH;
|
||||
double ratio = (double) MAX_WIDTH / (double) imageView.getWidth();
|
||||
tempImageHeight = (int) (imageView.getHeight() * ratio);
|
||||
imageWidth = MAX_WIDTH;
|
||||
double ratio = (double) MAX_WIDTH / (double) imageViewWidth;
|
||||
imageHeight = (int) (imageViewHeight * ratio);
|
||||
}
|
||||
|
||||
// Ensure space for padding if wanted
|
||||
imageWidth = tempImageWidth;
|
||||
imageHeight = tempImageHeight - padding;
|
||||
|
||||
this.showFallback = showFallback;
|
||||
}
|
||||
|
||||
@@ -94,12 +103,15 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
switch (format.format()) {
|
||||
// 2D barcodes
|
||||
case AZTEC:
|
||||
case DATA_MATRIX:
|
||||
case MAXICODE:
|
||||
case PDF_417:
|
||||
case QR_CODE:
|
||||
return MAX_WIDTH_2D;
|
||||
|
||||
// 2D but rectangular versions get blurry otherwise
|
||||
case DATA_MATRIX:
|
||||
return MAX_WIDTH_1D;
|
||||
|
||||
// 1D barcodes:
|
||||
case CODABAR:
|
||||
case CODE_39:
|
||||
@@ -261,6 +273,11 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
|
||||
if (result != null) {
|
||||
Log.i(TAG, "Displaying barcode");
|
||||
if (widthPadding) {
|
||||
imageView.setPadding(imagePadding / 2, 0, imagePadding / 2, 0);
|
||||
} else {
|
||||
imageView.setPadding(0, imagePadding / 2, 0, imagePadding / 2);
|
||||
}
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (isSuccesful) {
|
||||
|
||||
@@ -81,7 +81,7 @@ public class CardShortcutConfigure extends CatimaAppCompatActivity implements Lo
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
getMenuInflater().inflate(R.menu.card_details_menu, inputMenu);
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ public class CardShortcutConfigure extends CatimaAppCompatActivity implements Lo
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem) {
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
if (id == R.id.action_shown_details) {
|
||||
mAdapter.showSelectDetailDisplayDialog();
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -67,7 +67,6 @@ public class CatimaBarcode {
|
||||
|
||||
public boolean isSquare() {
|
||||
return mBarcodeFormat == BarcodeFormat.AZTEC
|
||||
|| mBarcodeFormat == BarcodeFormat.DATA_MATRIX
|
||||
|| mBarcodeFormat == BarcodeFormat.MAXICODE
|
||||
|| mBarcodeFormat == BarcodeFormat.QR_CODE;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,18 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DBHelper extends SQLiteOpenHelper {
|
||||
public static final String DATABASE_NAME = "Catima.db";
|
||||
public static final int ORIGINAL_DATABASE_VERSION = 1;
|
||||
public static final int DATABASE_VERSION = 16;
|
||||
|
||||
// NB: changing this value requires a migration
|
||||
public static final int DEFAULT_ZOOM_LEVEL = 100;
|
||||
|
||||
public static class LoyaltyCardDbGroups {
|
||||
public static final String TABLE = "groups";
|
||||
public static final String ID = "_id";
|
||||
@@ -106,7 +111,7 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
LoyaltyCardDbIds.BARCODE_TYPE + " TEXT," +
|
||||
LoyaltyCardDbIds.STAR_STATUS + " INTEGER DEFAULT '0'," +
|
||||
LoyaltyCardDbIds.LAST_USED + " INTEGER DEFAULT '0', " +
|
||||
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '100', " +
|
||||
LoyaltyCardDbIds.ZOOM_LEVEL + " INTEGER DEFAULT '" + DEFAULT_ZOOM_LEVEL + "', " +
|
||||
LoyaltyCardDbIds.ARCHIVE_STATUS + " INTEGER DEFAULT '0' )");
|
||||
|
||||
// create associative table for cards in groups
|
||||
@@ -323,6 +328,21 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> imageFiles(Context context, final SQLiteDatabase database) {
|
||||
Set<String> files = new HashSet<>();
|
||||
Cursor cardCursor = getLoyaltyCardCursor(database);
|
||||
while (cardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
String name = Utils.getCardImageFileName(card.id, imageLocationType);
|
||||
if (Utils.retrieveCardImageAsFile(context, name).exists()) {
|
||||
files.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private static ContentValues generateFTSContentValues(final int id, final String store, final String note) {
|
||||
// FTS on Android is severely limited and can only search for word starting with a certain string
|
||||
// So for each word, we grab every single substring
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -84,7 +85,7 @@ public class GroupCursorAdapter extends BaseCursorAdapter<GroupCursorAdapter.Gro
|
||||
|
||||
public static class GroupListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
public TextView mName, mCardCount;
|
||||
public ImageButton mMoveUp, mMoveDown, mEdit, mDelete;
|
||||
public Button mMoveUp, mMoveDown, mEdit, mDelete;
|
||||
|
||||
public GroupListItemViewHolder(GroupLayoutBinding groupLayoutBinding) {
|
||||
super(groupLayoutBinding.getRoot());
|
||||
|
||||
@@ -46,8 +46,11 @@ public class ImportURIHelper {
|
||||
}
|
||||
|
||||
private boolean isImportUri(Uri uri) {
|
||||
// Remove trailing slash added by some browsers (if it exists)
|
||||
final String uriPath = uri.getPath().replaceAll("/$", "");
|
||||
|
||||
for (int i = 0; i < hosts.length; i++) {
|
||||
if (uri.getHost().equals(hosts[i]) && uri.getPath().equals(paths[i])) {
|
||||
if (uri.getHost().equals(hosts[i]) && uriPath.equals(paths[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,30 +14,28 @@ public class LoyaltyCard implements Parcelable {
|
||||
public final int id;
|
||||
public final String store;
|
||||
public final String note;
|
||||
@Nullable
|
||||
public final Date validFrom;
|
||||
@Nullable
|
||||
public final Date expiry;
|
||||
public final BigDecimal balance;
|
||||
@Nullable
|
||||
public final Currency balanceType;
|
||||
public final String cardId;
|
||||
|
||||
@Nullable
|
||||
public final String barcodeId;
|
||||
|
||||
@Nullable
|
||||
public final CatimaBarcode barcodeType;
|
||||
|
||||
@Nullable
|
||||
public final Integer headerColor;
|
||||
|
||||
public final int starStatus;
|
||||
public final int archiveStatus;
|
||||
public final long lastUsed;
|
||||
public int zoomLevel;
|
||||
|
||||
public LoyaltyCard(final int id, final String store, final String note, final Date validFrom,
|
||||
final Date expiry, final BigDecimal balance, final Currency balanceType,
|
||||
final String cardId, @Nullable final String barcodeId,
|
||||
@Nullable final CatimaBarcode barcodeType,
|
||||
public LoyaltyCard(final int id, final String store, final String note, @Nullable final Date validFrom,
|
||||
@Nullable final Date expiry, final BigDecimal balance, @Nullable final Currency balanceType,
|
||||
final String cardId, @Nullable final String barcodeId, @Nullable final CatimaBarcode barcodeType,
|
||||
@Nullable final Integer headerColor, final int starStatus,
|
||||
final long lastUsed, final int zoomLevel, final int archiveStatus) {
|
||||
this.id = id;
|
||||
@@ -145,6 +143,24 @@ public class LoyaltyCard implements Parcelable {
|
||||
return new LoyaltyCard(id, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed, zoomLevel, archived);
|
||||
}
|
||||
|
||||
public static boolean isDuplicate(final LoyaltyCard a, final LoyaltyCard b) {
|
||||
// Skip lastUsed & zoomLevel
|
||||
return a.id == b.id && // non-nullable int
|
||||
a.store.equals(b.store) && // non-nullable String
|
||||
a.note.equals(b.note) && // non-nullable String
|
||||
Utils.equals(a.validFrom, b.validFrom) && // nullable Date
|
||||
Utils.equals(a.expiry, b.expiry) && // nullable Date
|
||||
a.balance.equals(b.balance) && // non-nullable BigDecimal
|
||||
Utils.equals(a.balanceType, b.balanceType) && // nullable Currency
|
||||
a.cardId.equals(b.cardId) && // non-nullable String
|
||||
Utils.equals(a.barcodeId, b.barcodeId) && // nullable String
|
||||
Utils.equals(a.barcodeType == null ? null : a.barcodeType.format(),
|
||||
b.barcodeType == null ? null : b.barcodeType.format()) && // nullable CatimaBarcode with no overridden .equals(), so we need to check .format()
|
||||
Utils.equals(a.headerColor, b.headerColor) && // nullable Integer
|
||||
a.starStatus == b.starStatus && // non-nullable int
|
||||
a.archiveStatus == b.archiveStatus; // non-nullable int
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return id;
|
||||
|
||||
@@ -18,12 +18,14 @@ import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat;
|
||||
@@ -31,7 +33,6 @@ import androidx.core.graphics.BlendModeCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import protect.card_locker.databinding.LoyaltyCardLayoutBinding;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
||||
private int mCurrentSelectedIndex = -1;
|
||||
@@ -41,7 +42,10 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
protected SparseBooleanArray mSelectedItems;
|
||||
protected SparseBooleanArray mAnimationItemsIndex;
|
||||
private boolean mReverseAllAnimations = false;
|
||||
private boolean mShowDetails;
|
||||
private boolean mShowNameBelowThumbnail;
|
||||
private boolean mShowNote;
|
||||
private boolean mShowBalance;
|
||||
private boolean mShowValidity;
|
||||
|
||||
public LoyaltyCardCursorAdapter(Context inputContext, Cursor inputCursor, CardAdapterListener inputListener) {
|
||||
super(inputCursor, DBHelper.LoyaltyCardDbIds.ID);
|
||||
@@ -58,29 +62,100 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
swapCursor(inputCursor);
|
||||
}
|
||||
|
||||
private void saveDetailState(int stateId, boolean value) {
|
||||
SharedPreferences cardDetailsPref = mContext.getSharedPreferences(
|
||||
mContext.getString(R.string.sharedpreference_card_details),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor cardDetailsPrefEditor = cardDetailsPref.edit();
|
||||
cardDetailsPrefEditor.putBoolean(mContext.getString(stateId), value);
|
||||
cardDetailsPrefEditor.apply();
|
||||
}
|
||||
|
||||
public void refreshState() {
|
||||
// Retrieve user details preference
|
||||
SharedPreferences cardDetailsPref = mContext.getSharedPreferences(
|
||||
mContext.getString(R.string.sharedpreference_card_details),
|
||||
Context.MODE_PRIVATE);
|
||||
mShowDetails = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show), true);
|
||||
mShowNameBelowThumbnail = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show_name_below_thumbnail), false);
|
||||
mShowNote = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show_note), true);
|
||||
mShowBalance = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show_balance), true);
|
||||
mShowValidity = cardDetailsPref.getBoolean(mContext.getString(R.string.sharedpreference_card_details_show_validity), true);
|
||||
}
|
||||
|
||||
public void showDetails(boolean show) {
|
||||
mShowDetails = show;
|
||||
public void showNameBelowThumbnail(boolean show) {
|
||||
mShowNameBelowThumbnail = show;
|
||||
notifyDataSetChanged();
|
||||
|
||||
// Store in Shared Preference to restore next adapter launch
|
||||
SharedPreferences cardDetailsPref = mContext.getSharedPreferences(
|
||||
mContext.getString(R.string.sharedpreference_card_details),
|
||||
Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor cardDetailsPrefEditor = cardDetailsPref.edit();
|
||||
cardDetailsPrefEditor.putBoolean(mContext.getString(R.string.sharedpreference_card_details_show), show);
|
||||
cardDetailsPrefEditor.apply();
|
||||
saveDetailState(R.string.sharedpreference_card_details_show_name_below_thumbnail, show);
|
||||
}
|
||||
|
||||
public boolean showingDetails() {
|
||||
return mShowDetails;
|
||||
public boolean showingNameBelowThumbnail() {
|
||||
return mShowNameBelowThumbnail;
|
||||
}
|
||||
|
||||
public void showNote(boolean show) {
|
||||
mShowNote = show;
|
||||
notifyDataSetChanged();
|
||||
|
||||
saveDetailState(R.string.sharedpreference_card_details_show_note, show);
|
||||
}
|
||||
|
||||
public boolean showingNote() {
|
||||
return mShowNote;
|
||||
}
|
||||
|
||||
public void showBalance(boolean show) {
|
||||
mShowBalance = show;
|
||||
notifyDataSetChanged();
|
||||
|
||||
saveDetailState(R.string.sharedpreference_card_details_show_balance, show);
|
||||
}
|
||||
|
||||
public boolean showingBalance() {
|
||||
return mShowBalance;
|
||||
}
|
||||
|
||||
public void showValidity(boolean show) {
|
||||
mShowValidity = show;
|
||||
notifyDataSetChanged();
|
||||
|
||||
saveDetailState(R.string.sharedpreference_card_details_show_validity, show);
|
||||
}
|
||||
|
||||
public boolean showingValidity() {
|
||||
return mShowValidity;
|
||||
}
|
||||
|
||||
public void showSelectDetailDisplayDialog() {
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(mContext);
|
||||
builder.setTitle(R.string.action_show_details);
|
||||
builder.setMultiChoiceItems(
|
||||
new String[]{
|
||||
mContext.getString(R.string.show_name_below_image_thumbnail),
|
||||
mContext.getString(R.string.show_note),
|
||||
mContext.getString(R.string.show_balance),
|
||||
mContext.getString(R.string.show_validity)
|
||||
},
|
||||
new boolean[]{
|
||||
showingNameBelowThumbnail(),
|
||||
showingNote(),
|
||||
showingBalance(),
|
||||
showingValidity()
|
||||
},
|
||||
(dialogInterface, i, b) -> {
|
||||
switch (i) {
|
||||
case 0: showNameBelowThumbnail(b); break;
|
||||
case 1: showNote(b); break;
|
||||
case 2: showBalance(b); break;
|
||||
case 3: showValidity(b); break;
|
||||
default: throw new IndexOutOfBoundsException("No such index exists in LoyaltyCardCursorAdapter show details view");
|
||||
}
|
||||
}
|
||||
);
|
||||
builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss());
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -104,34 +179,41 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
inputHolder.mDivider.setVisibility(View.GONE);
|
||||
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
|
||||
Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon);
|
||||
|
||||
if (mShowDetails && !loyaltyCard.note.isEmpty()) {
|
||||
if (mShowNameBelowThumbnail && icon != null) {
|
||||
inputHolder.setStoreField(loyaltyCard.store);
|
||||
} else {
|
||||
inputHolder.setStoreField(null);
|
||||
}
|
||||
|
||||
if (mShowNote && !loyaltyCard.note.isEmpty()) {
|
||||
inputHolder.setNoteField(loyaltyCard.note);
|
||||
} else {
|
||||
inputHolder.setNoteField(null);
|
||||
}
|
||||
|
||||
if (mShowDetails && !loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
if (mShowBalance && !loyaltyCard.balance.equals(new BigDecimal("0"))) {
|
||||
inputHolder.setExtraField(inputHolder.mBalanceField, Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType), null);
|
||||
} else {
|
||||
inputHolder.setExtraField(inputHolder.mBalanceField, null, null);
|
||||
}
|
||||
|
||||
if (mShowDetails && loyaltyCard.validFrom != null) {
|
||||
if (mShowValidity && loyaltyCard.validFrom != null) {
|
||||
inputHolder.setExtraField(inputHolder.mValidFromField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.validFrom), Utils.isNotYetValid(loyaltyCard.validFrom) ? Color.RED : null);
|
||||
} else {
|
||||
inputHolder.setExtraField(inputHolder.mValidFromField, null, null);
|
||||
}
|
||||
|
||||
if (mShowDetails && loyaltyCard.expiry != null) {
|
||||
if (mShowValidity && loyaltyCard.expiry != null) {
|
||||
inputHolder.setExtraField(inputHolder.mExpiryField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry), Utils.hasExpired(loyaltyCard.expiry) ? Color.RED : null);
|
||||
} else {
|
||||
inputHolder.setExtraField(inputHolder.mExpiryField, null, null);
|
||||
}
|
||||
|
||||
inputHolder.mCardIcon.setContentDescription(loyaltyCard.store);
|
||||
Utils.setIconOrTextWithBackground(mContext, loyaltyCard, inputHolder.mCardIcon, inputHolder.mCardText);
|
||||
inputHolder.setIconBackgroundColor(loyaltyCard.headerColor != null ? loyaltyCard.headerColor : androidx.appcompat.R.attr.colorPrimary);
|
||||
Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText);
|
||||
inputHolder.setIconBackgroundColor(Utils.getHeaderColor(mContext, loyaltyCard));
|
||||
|
||||
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
||||
|
||||
@@ -218,7 +300,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
|
||||
public class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView mCardText, mNoteField, mBalanceField, mValidFromField, mExpiryField;
|
||||
public TextView mCardText, mStoreField, mNoteField, mBalanceField, mValidFromField, mExpiryField;
|
||||
public ImageView mCardIcon, mStarBackground, mStarBorder, mTickIcon, mArchivedBackground;
|
||||
public MaterialCardView mRow;
|
||||
public ConstraintLayout mStar, mArchived;
|
||||
@@ -231,6 +313,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
View inputView = loyaltyCardLayoutBinding.getRoot();
|
||||
mRow = loyaltyCardLayoutBinding.row;
|
||||
mDivider = loyaltyCardLayoutBinding.infoDivider;
|
||||
mStoreField = loyaltyCardLayoutBinding.store;
|
||||
mNoteField = loyaltyCardLayoutBinding.note;
|
||||
mBalanceField = loyaltyCardLayoutBinding.balance;
|
||||
mValidFromField = loyaltyCardLayoutBinding.validFrom;
|
||||
@@ -280,6 +363,16 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
field.requestLayout();
|
||||
}
|
||||
|
||||
public void setStoreField(String text) {
|
||||
if (text == null) {
|
||||
mStoreField.setVisibility(View.GONE);
|
||||
} else {
|
||||
mStoreField.setVisibility(View.VISIBLE);
|
||||
mStoreField.setText(text);
|
||||
}
|
||||
mStoreField.requestLayout();
|
||||
}
|
||||
|
||||
public void setNoteField(String text) {
|
||||
if (text == null) {
|
||||
mNoteField.setVisibility(View.GONE);
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
@@ -30,10 +31,25 @@ import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
@@ -65,20 +81,9 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import protect.card_locker.async.TaskHandler;
|
||||
import protect.card_locker.databinding.LayoutChipChoiceBinding;
|
||||
import protect.card_locker.databinding.LoyaltyCardEditActivityBinding;
|
||||
@@ -98,6 +103,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
private final String STATE_FRONT_IMAGE_REMOVED = "frontImageRemoved";
|
||||
private final String STATE_BACK_IMAGE_REMOVED = "backImageRemoved";
|
||||
private final String STATE_ICON_REMOVED = "iconRemoved";
|
||||
private final String STATE_OPEN_SET_ICON_MENU = "openSetIconMenu";
|
||||
|
||||
private final String TEMP_CAMERA_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_camera_image.jpg";
|
||||
private final String TEMP_CROP_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_crop_image.png";
|
||||
@@ -118,6 +124,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
public static final String BUNDLE_ID = "id";
|
||||
public static final String BUNDLE_DUPLICATE_ID = "duplicateId";
|
||||
public static final String BUNDLE_UPDATE = "update";
|
||||
public static final String BUNDLE_OPEN_SET_ICON_MENU = "openSetIconMenu";
|
||||
public static final String BUNDLE_CARDID = "cardId";
|
||||
public static final String BUNDLE_BARCODEID = "barcodeId";
|
||||
public static final String BUNDLE_BARCODETYPE = "barcodeType";
|
||||
@@ -152,6 +159,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
int loyaltyCardId;
|
||||
boolean updateLoyaltyCard;
|
||||
boolean duplicateFromLoyaltyCardId;
|
||||
boolean openSetIconMenu;
|
||||
String cardId;
|
||||
String barcodeId;
|
||||
String barcodeType;
|
||||
@@ -170,6 +178,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
boolean validBalance = true;
|
||||
HashMap<String, Currency> currencies = new HashMap<>();
|
||||
HashMap<String, String> currencySymbols = new HashMap<>();
|
||||
|
||||
LoyaltyCard tempLoyaltyCard;
|
||||
|
||||
@@ -238,6 +247,8 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
updateLoyaltyCard = b != null && b.getBoolean(BUNDLE_UPDATE, false);
|
||||
duplicateFromLoyaltyCardId = b != null && b.getBoolean(BUNDLE_DUPLICATE_ID, false);
|
||||
|
||||
openSetIconMenu = b != null && b.getBoolean(BUNDLE_OPEN_SET_ICON_MENU, false);
|
||||
|
||||
cardId = b != null ? b.getString(BUNDLE_CARDID) : null;
|
||||
barcodeId = b != null ? b.getString(BUNDLE_BARCODEID) : null;
|
||||
barcodeType = b != null ? b.getString(BUNDLE_BARCODETYPE) : null;
|
||||
@@ -284,6 +295,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
savedInstanceState.putInt(STATE_FRONT_IMAGE_REMOVED, mFrontImageRemoved ? 1 : 0);
|
||||
savedInstanceState.putInt(STATE_BACK_IMAGE_REMOVED, mBackImageRemoved ? 1 : 0);
|
||||
savedInstanceState.putInt(STATE_ICON_REMOVED, mIconRemoved ? 1 : 0);
|
||||
savedInstanceState.putInt(STATE_OPEN_SET_ICON_MENU, openSetIconMenu ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -301,6 +313,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
mFrontImageRemoved = savedInstanceState.getInt(STATE_FRONT_IMAGE_REMOVED) == 1;
|
||||
mBackImageRemoved = savedInstanceState.getInt(STATE_BACK_IMAGE_REMOVED) == 1;
|
||||
mIconRemoved = savedInstanceState.getInt(STATE_ICON_REMOVED) == 1;
|
||||
openSetIconMenu = savedInstanceState.getInt(STATE_OPEN_SET_ICON_MENU) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -320,6 +333,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
for (Currency currency : Currency.getAvailableCurrencies()) {
|
||||
currencies.put(currency.getSymbol(), currency);
|
||||
currencySymbols.put(currency.getCurrencyCode(), currency.getSymbol());
|
||||
}
|
||||
|
||||
tabs = binding.tabs;
|
||||
@@ -365,6 +379,19 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
addDateFieldTextChangedListener(expiryField, R.string.never, R.string.chooseExpiryDate, LoyaltyCardField.expiry);
|
||||
|
||||
DatePickerFragment.registerDatePickListener(this, (textFieldToEdit, newDate) -> {
|
||||
switch (textFieldToEdit) {
|
||||
case validFrom:
|
||||
formatDateField(this, validFromField, newDate);
|
||||
break;
|
||||
case expiry:
|
||||
formatDateField(this, expiryField, newDate);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unexpected field: " + textFieldToEdit);
|
||||
}
|
||||
});
|
||||
|
||||
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(tempLoyaltyCard.balance, tempLoyaltyCard.balanceType));
|
||||
@@ -484,10 +511,20 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
builder.setTitle(R.string.setBarcodeId);
|
||||
final EditText input = new EditText(LoyaltyCardEditActivity.this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
FrameLayout container = new FrameLayout(LoyaltyCardEditActivity.this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
int contentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
params.leftMargin = contentPadding;
|
||||
params.topMargin = contentPadding / 2;
|
||||
params.rightMargin = contentPadding;
|
||||
|
||||
input.setLayoutParams(params);
|
||||
container.addView(input);
|
||||
if (tempLoyaltyCard.barcodeId != null) {
|
||||
input.setText(tempLoyaltyCard.barcodeId);
|
||||
}
|
||||
builder.setView(input);
|
||||
builder.setView(container);
|
||||
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
// If the user manually changes the barcode again make sure we disable the
|
||||
@@ -834,10 +871,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
// Generate random header color
|
||||
if (tempLoyaltyCard.headerColor == null) {
|
||||
// Select a random color to start out with.
|
||||
updateTempState(LoyaltyCardField.headerColor, Utils.getRandomHeaderColor(this));
|
||||
// If name is set, pick colour relevant for name. Otherwise pick randomly
|
||||
updateTempState(LoyaltyCardField.headerColor, tempLoyaltyCard.store.isEmpty() ? Utils.getRandomHeaderColor(this) : Utils.getHeaderColor(this, tempLoyaltyCard));
|
||||
}
|
||||
|
||||
// Update from intent
|
||||
@@ -897,12 +933,19 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
}
|
||||
|
||||
onResuming = false;
|
||||
|
||||
// Fake click on the edit icon to cause the set icon option to pop up if the icon was
|
||||
// long-pressed in the view activity
|
||||
if (openSetIconMenu) {
|
||||
openSetIconMenu = false;
|
||||
thumbnail.callOnClick();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setColorFromIcon() {
|
||||
Object icon = thumbnail.getTag();
|
||||
if (icon != null && (icon instanceof Bitmap)) {
|
||||
int headerColor = Utils.getHeaderColorFromImage((Bitmap) icon, tempLoyaltyCard.headerColor != null ? tempLoyaltyCard.headerColor : androidx.appcompat.R.attr.colorPrimary);
|
||||
int headerColor = Utils.getHeaderColorFromImage((Bitmap) icon, Utils.getHeaderColor(this, tempLoyaltyCard));
|
||||
|
||||
updateTempState(LoyaltyCardField.headerColor, headerColor);
|
||||
|
||||
@@ -940,9 +983,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
if (!lastValue.toString().equals(getString(chooseDateOptionStringId))) {
|
||||
dateField.setText(lastValue);
|
||||
}
|
||||
DialogFragment datePickerFragment = new DatePickerFragment(
|
||||
LoyaltyCardEditActivity.this,
|
||||
dateField,
|
||||
DialogFragment datePickerFragment = DatePickerFragment.newInstance(
|
||||
loyaltyCardField,
|
||||
(Date) dateField.getTag(),
|
||||
// if the expiry date is being set, set date picker's minDate to the 'valid from' date
|
||||
loyaltyCardField == LoyaltyCardField.expiry ? (Date) validFromField.getTag() : null,
|
||||
// if the 'valid from' date is being set, set date picker's maxDate to the expiry date
|
||||
@@ -986,7 +1029,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
if (balanceType == null) {
|
||||
balanceCurrencyField.setText(getString(R.string.points));
|
||||
} else {
|
||||
balanceCurrencyField.setText(balanceType.getSymbol());
|
||||
balanceCurrencyField.setText(getCurrencySymbol(balanceType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,27 +1352,54 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
public static class DatePickerFragment extends DialogFragment
|
||||
implements DatePickerDialog.OnDateSetListener {
|
||||
|
||||
final Context context;
|
||||
final EditText textFieldEdit;
|
||||
@Nullable
|
||||
final Date minDate;
|
||||
@Nullable
|
||||
final Date maxDate;
|
||||
public interface OnDatePickListener {
|
||||
void onDatePicked(@NonNull LoyaltyCardField textFieldToEdit, @NonNull Date newDate);
|
||||
}
|
||||
|
||||
DatePickerFragment(Context context, EditText textFieldEdit, @Nullable Date minDate, @Nullable Date maxDate) {
|
||||
this.context = context;
|
||||
this.textFieldEdit = textFieldEdit;
|
||||
this.minDate = minDate;
|
||||
this.maxDate = maxDate;
|
||||
private static final String TEXT_FIELD_TO_EDIT_ARGUMENT_KEY = "text_field_to_edit";
|
||||
private static final String CURRENT_DATE_ARGUMENT_KEY = "current_date";
|
||||
private static final String MIN_DATE_ARGUMENT_KEY = "min_date";
|
||||
private static final String MAX_DATE_ARGUMENT_KEY = "max_date";
|
||||
private static final String PICK_DATE_REQUEST_KEY = "pick_date_request";
|
||||
private static final String NEWLY_PICKED_DATE_ARGUMENT_KEY = "newly_picked_date";
|
||||
|
||||
LoyaltyCardField textFieldEdit;
|
||||
@Nullable
|
||||
Date minDate;
|
||||
@Nullable
|
||||
Date maxDate;
|
||||
|
||||
public static DatePickerFragment newInstance(@NonNull LoyaltyCardField textField, @Nullable Date currentDate, @Nullable Date minDate, @Nullable Date maxDate) {
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textField);
|
||||
args.putSerializable(CURRENT_DATE_ARGUMENT_KEY, currentDate);
|
||||
args.putSerializable(MIN_DATE_ARGUMENT_KEY, minDate);
|
||||
args.putSerializable(MAX_DATE_ARGUMENT_KEY, maxDate);
|
||||
DatePickerFragment fragment = new DatePickerFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static void registerDatePickListener(@NonNull AppCompatActivity activity, @NonNull OnDatePickListener listener) {
|
||||
activity.getSupportFragmentManager().setFragmentResultListener(
|
||||
PICK_DATE_REQUEST_KEY,
|
||||
activity,
|
||||
(requestKey, result) -> listener.onDatePicked(
|
||||
(LoyaltyCardField) Objects.requireNonNull(result.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY)),
|
||||
(Date) Objects.requireNonNull(result.getSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY))));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Bundle args = requireArguments();
|
||||
textFieldEdit = (LoyaltyCardField) args.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY);
|
||||
minDate = (Date) args.getSerializable(MIN_DATE_ARGUMENT_KEY);
|
||||
maxDate = (Date) args.getSerializable(MAX_DATE_ARGUMENT_KEY);
|
||||
// Use the current date as the default date in the picker
|
||||
final Calendar c = Calendar.getInstance();
|
||||
|
||||
Date date = (Date) textFieldEdit.getTag();
|
||||
Date date = (Date) args.getSerializable(CURRENT_DATE_ARGUMENT_KEY);
|
||||
if (date != null) {
|
||||
c.setTime(date);
|
||||
}
|
||||
@@ -1371,7 +1441,10 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
Date date = new Date(unixTime);
|
||||
|
||||
formatDateField(context, textFieldEdit, date);
|
||||
Bundle result = new Bundle();
|
||||
result.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textFieldEdit);
|
||||
result.putSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY, date);
|
||||
getParentFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1614,11 +1687,16 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
private void currencyPrioritizeLocaleSymbols(ArrayList<String> currencyList, Locale locale) {
|
||||
try {
|
||||
String currencySymbol = Currency.getInstance(locale).getSymbol();
|
||||
String currencySymbol = getCurrencySymbol(Currency.getInstance(locale));
|
||||
currencyList.remove(currencySymbol);
|
||||
currencyList.add(0, currencySymbol);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Could not get currency data for locale info: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getCurrencySymbol(final Currency currency) {
|
||||
// Workaround for Android bug where the output of Currency.getSymbol() changes.
|
||||
return currencySymbols.get(currency.getCurrencyCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
@@ -288,8 +289,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
binding.fabEdit.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCardId);
|
||||
bundle.putBoolean("update", true);
|
||||
bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, loyaltyCardId);
|
||||
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_UPDATE, true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
@@ -301,6 +302,20 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
binding.bottomAppBarNextButton.setOnClickListener(view -> prevNextCard(true));
|
||||
binding.bottomAppBarUpdateBalanceButton.setOnClickListener(view -> showBalanceUpdateDialog());
|
||||
|
||||
binding.iconContainer.setOnClickListener(view -> Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show());
|
||||
binding.iconContainer.setOnLongClickListener(view -> {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, loyaltyCardId);
|
||||
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_UPDATE, true);
|
||||
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_OPEN_SET_ICON_MENU, true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
binding.mainImage.setOnClickListener(view -> onMainImageTap());
|
||||
// This long-press was originally only intended for when Talkback was used but sadly limiting
|
||||
// this doesn't seem to work well
|
||||
@@ -327,22 +342,12 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
AlertDialog.Builder infoDialog = new MaterialAlertDialogBuilder(this);
|
||||
|
||||
int dialogContentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
int dialogTitlePadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_title_padding);
|
||||
TextView infoTitleView = new TextView(this);
|
||||
infoTitleView.setPadding(
|
||||
dialogContentPadding,
|
||||
dialogContentPadding,
|
||||
dialogContentPadding,
|
||||
dialogTitlePadding
|
||||
);
|
||||
infoTitleView.setText(loyaltyCard.store);
|
||||
infoDialog.setCustomTitle(infoTitleView);
|
||||
infoDialog.setTitle(loyaltyCard.store);
|
||||
|
||||
TextView infoTextview = new TextView(this);
|
||||
infoTextview.setPadding(
|
||||
dialogContentPadding,
|
||||
0,
|
||||
dialogContentPadding / 2,
|
||||
dialogContentPadding,
|
||||
0
|
||||
);
|
||||
@@ -405,6 +410,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
);
|
||||
int contentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
params.leftMargin = contentPadding;
|
||||
params.topMargin = contentPadding / 2;
|
||||
params.rightMargin = contentPadding;
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
@@ -583,7 +589,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
loyaltyCardGroups = DBHelper.getLoyaltyCardGroups(database, loyaltyCardId);
|
||||
|
||||
setupOrientation();
|
||||
showHideElementsForScreenSize();
|
||||
|
||||
format = loyaltyCard.barcodeType;
|
||||
cardIdString = loyaltyCard.cardId;
|
||||
@@ -591,12 +597,23 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
binding.cardIdView.setText(loyaltyCard.cardId);
|
||||
|
||||
int backgroundHeaderColor;
|
||||
if (loyaltyCard.headerColor != null) {
|
||||
backgroundHeaderColor = loyaltyCard.headerColor;
|
||||
} else {
|
||||
backgroundHeaderColor = LetterBitmap.getDefaultColor(this, loyaltyCard.store);
|
||||
}
|
||||
// Display full text on click in case it doesn't fit in a single line
|
||||
binding.cardIdView.setOnClickListener(v -> {
|
||||
TextView cardIdView = new TextView(LoyaltyCardViewActivity.this);
|
||||
cardIdView.setText(loyaltyCard.cardId);
|
||||
cardIdView.setTextIsSelectable(true);
|
||||
int contentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
cardIdView.setPadding(contentPadding, contentPadding / 2, contentPadding, 0);
|
||||
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(LoyaltyCardViewActivity.this);
|
||||
builder.setTitle(R.string.cardId);
|
||||
builder.setView(cardIdView);
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
});
|
||||
|
||||
int backgroundHeaderColor = Utils.getHeaderColor(this, loyaltyCard);
|
||||
|
||||
// Also apply colours to UI elements
|
||||
int darkenedColor = ColorUtils.blendARGB(backgroundHeaderColor, Color.BLACK, 0.1f);
|
||||
@@ -610,7 +627,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
editButtonIcon.setTint(Utils.needsDarkForeground(complementaryColor) ? Color.BLACK : Color.WHITE);
|
||||
binding.fabEdit.setImageDrawable(editButtonIcon);
|
||||
|
||||
Utils.setIconOrTextWithBackground(this, loyaltyCard, binding.iconImage, binding.iconText);
|
||||
Bitmap icon = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon);
|
||||
Utils.setIconOrTextWithBackground(this, loyaltyCard, icon, binding.iconImage, binding.iconText);
|
||||
|
||||
// If the background is very bright, we should use dark icons
|
||||
backgroundNeedsDarkIcons = Utils.needsDarkForeground(backgroundHeaderColor);
|
||||
@@ -743,8 +761,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
} else if (id == R.id.action_duplicate) {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("id", loyaltyCardId);
|
||||
bundle.putBoolean("duplicateId", true);
|
||||
bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, loyaltyCardId);
|
||||
bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_DUPLICATE_ID, true);
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
|
||||
@@ -799,13 +817,17 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void setupOrientation() {
|
||||
private void showHideElementsForScreenSize() {
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Log.d(TAG, "Detected landscape mode");
|
||||
// Detect square-ish screens like the Unihertz Titan
|
||||
boolean isSmallHeight = getResources().getDisplayMetrics().heightPixels < (getResources().getDisplayMetrics().widthPixels * 1.5);
|
||||
|
||||
// Treat sqaure-ish screens as
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE || isSmallHeight) {
|
||||
Log.d(TAG, "Detected landscape mode or square-ish screen");
|
||||
binding.iconContainer.setVisibility(View.GONE);
|
||||
} else {
|
||||
Log.d(TAG, "Detected portrait mode");
|
||||
Log.d(TAG, "Detected portrait mode on non square-ish screen");
|
||||
binding.iconContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -847,18 +869,17 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
private void drawMainImage(int index, boolean waitForResize, boolean isFullscreen) {
|
||||
if (imageTypes.isEmpty()) {
|
||||
barcodeRenderTarget.setVisibility(View.GONE);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
binding.cardIdView.setTextColor(MaterialColors.getColor(binding.cardIdView, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
return;
|
||||
}
|
||||
|
||||
ImageType wantedImageType = imageTypes.get(index);
|
||||
|
||||
if (wantedImageType == ImageType.BARCODE) {
|
||||
// Use border in non-fullscreen mode
|
||||
if (!isFullscreen) {
|
||||
barcodeRenderTarget.setBackground(AppCompatResources.getDrawable(this, R.drawable.round_outline));
|
||||
} else {
|
||||
barcodeRenderTarget.setBackgroundColor(Color.WHITE);
|
||||
}
|
||||
barcodeRenderTarget.setBackgroundColor(Color.WHITE);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.WHITE);
|
||||
binding.cardIdView.setTextColor(getResources().getColor(R.color.md_theme_light_onSurfaceVariant));
|
||||
|
||||
if (waitForResize) {
|
||||
redrawBarcodeAfterResize(!isFullscreen);
|
||||
@@ -870,10 +891,14 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
} else if (wantedImageType == ImageType.IMAGE_FRONT) {
|
||||
barcodeRenderTarget.setImageBitmap(frontImageBitmap);
|
||||
barcodeRenderTarget.setBackgroundColor(Color.TRANSPARENT);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
binding.cardIdView.setTextColor(MaterialColors.getColor(binding.cardIdView, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
barcodeRenderTarget.setContentDescription(getString(R.string.frontImageDescription));
|
||||
} else if (wantedImageType == ImageType.IMAGE_BACK) {
|
||||
barcodeRenderTarget.setImageBitmap(backImageBitmap);
|
||||
barcodeRenderTarget.setBackgroundColor(Color.TRANSPARENT);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
binding.cardIdView.setTextColor(MaterialColors.getColor(binding.cardIdView, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
barcodeRenderTarget.setContentDescription(getString(R.string.backImageDescription));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown image type: " + wantedImageType);
|
||||
@@ -959,22 +984,33 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
return;
|
||||
}
|
||||
|
||||
final ImageButton prevButton;
|
||||
final ImageButton nextButton;
|
||||
|
||||
if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
prevButton = binding.mainRightButton;
|
||||
nextButton = binding.mainLeftButton;
|
||||
} else {
|
||||
prevButton = binding.mainLeftButton;
|
||||
nextButton = binding.mainRightButton;
|
||||
}
|
||||
|
||||
// Enable left button if we can go further left
|
||||
if (mainImageIndex > 0) {
|
||||
binding.mainLeftButton.setVisibility(View.VISIBLE);
|
||||
binding.mainLeftButton.setOnClickListener(view -> setMainImage(false, false));
|
||||
prevButton.setVisibility(View.VISIBLE);
|
||||
prevButton.setOnClickListener(view -> setMainImage(false, false));
|
||||
} else {
|
||||
binding.mainLeftButton.setVisibility(View.INVISIBLE);
|
||||
binding.mainLeftButton.setOnClickListener(null);
|
||||
prevButton.setVisibility(View.INVISIBLE);
|
||||
prevButton.setOnClickListener(null);
|
||||
}
|
||||
|
||||
// Enable right button if we can go further right
|
||||
if (mainImageIndex < (imageTypes.size() - 1)) {
|
||||
binding.mainRightButton.setVisibility(View.VISIBLE);
|
||||
binding.mainRightButton.setOnClickListener(view -> setMainImage(true, false));
|
||||
nextButton.setVisibility(View.VISIBLE);
|
||||
nextButton.setOnClickListener(view -> setMainImage(true, false));
|
||||
} else {
|
||||
binding.mainRightButton.setVisibility(View.INVISIBLE);
|
||||
binding.mainRightButton.setOnClickListener(null);
|
||||
nextButton.setVisibility(View.INVISIBLE);
|
||||
nextButton.setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,18 +15,15 @@ import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
@@ -435,7 +432,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
|
||||
private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
|
||||
for (int id : new int[]{R.id.action_search, R.id.action_unfold, R.id.action_sort}) {
|
||||
for (int id : new int[]{R.id.action_search, R.id.action_shown_details, R.id.action_sort}) {
|
||||
menu.findItem(id).setVisible(shouldShow);
|
||||
}
|
||||
}
|
||||
@@ -600,7 +597,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
getMenuInflater().inflate(R.menu.archive_menu, inputMenu);
|
||||
}
|
||||
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
displayCardSetupOptions(inputMenu, mLoyaltyCardCount > 0);
|
||||
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
@@ -653,8 +649,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
onBackPressed();
|
||||
}
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
if (id == R.id.action_shown_details) {
|
||||
mAdapter.showSelectDetailDisplayDialog();
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -160,7 +159,7 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
getMenuInflater().inflate(R.menu.card_details_menu, inputMenu);
|
||||
Utils.updateMenuCardDetailsButtonState(inputMenu.findItem(R.id.action_unfold), mAdapter.showingDetails());
|
||||
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
}
|
||||
|
||||
@@ -168,8 +167,8 @@ public class ManageGroupActivity extends CatimaAppCompatActivity implements Mana
|
||||
public boolean onOptionsItemSelected(MenuItem inputItem) {
|
||||
int id = inputItem.getItemId();
|
||||
|
||||
if (id == R.id.action_unfold) {
|
||||
mAdapter.showDetails(!mAdapter.showingDetails());
|
||||
if (id == R.id.action_shown_details) {
|
||||
mAdapter.showSelectDetailDisplayDialog();
|
||||
invalidateOptionsMenu();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -150,8 +150,10 @@ public class ManageGroupsActivity extends CatimaAppCompatActivity implements Gro
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.leftMargin = 50;
|
||||
params.rightMargin = 50;
|
||||
int contentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
params.leftMargin = contentPadding;
|
||||
params.topMargin = contentPadding / 2;
|
||||
params.rightMargin = contentPadding;
|
||||
input.setLayoutParams(params);
|
||||
container.addView(input);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ class ShortcutHelper {
|
||||
private static final int ADAPTIVE_BITMAP_SIZE = 108 * ADAPTIVE_BITMAP_SCALE;
|
||||
private static final int ADAPTIVE_BITMAP_VISIBLE_SIZE = 72 * ADAPTIVE_BITMAP_SCALE;
|
||||
private static final int ADAPTIVE_BITMAP_IMAGE_SIZE = ADAPTIVE_BITMAP_VISIBLE_SIZE + 5 * ADAPTIVE_BITMAP_SCALE;
|
||||
private static final int PADDING_COLOR = Color.argb(255, 255, 255, 255);
|
||||
private static final int PADDING_COLOR_OVERLAY = Color.argb(127, 0, 0, 0);
|
||||
|
||||
/**
|
||||
@@ -71,19 +70,13 @@ class ShortcutHelper {
|
||||
ShortcutInfoCompat found = list.remove(foundIndex.intValue());
|
||||
list.addFirst(found);
|
||||
} else {
|
||||
// The item is new to the list. First, we need to trim the list
|
||||
// until it is able to accept a new item, then the item is
|
||||
// inserted.
|
||||
while (list.size() >= MAX_SHORTCUTS) {
|
||||
list.pollLast();
|
||||
}
|
||||
|
||||
// The item is new to the list. We add it and trim the list later.
|
||||
ShortcutInfoCompat shortcut = createShortcutBuilder(context, card).build();
|
||||
|
||||
list.addFirst(shortcut);
|
||||
}
|
||||
|
||||
LinkedList<ShortcutInfoCompat> finalList = new LinkedList<>();
|
||||
int rank = 0;
|
||||
|
||||
// The ranks are now updated; the order in the list is the rank.
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
@@ -91,11 +84,20 @@ class ShortcutHelper {
|
||||
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(database, Integer.parseInt(prevShortcut.getId()));
|
||||
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(index)
|
||||
.build();
|
||||
// skip outdated cards that no longer exist
|
||||
if (loyaltyCard != null) {
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(rank)
|
||||
.build();
|
||||
|
||||
finalList.addLast(updatedShortcut);
|
||||
finalList.addLast(updatedShortcut);
|
||||
rank++;
|
||||
|
||||
// trim the list
|
||||
if (rank >= MAX_SHORTCUTS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, finalList);
|
||||
@@ -145,7 +147,7 @@ class ShortcutHelper {
|
||||
if (iconBitmap == null) {
|
||||
iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
|
||||
} else {
|
||||
iconBitmap = createAdaptiveBitmap(iconBitmap, loyaltyCard.headerColor == null ? PADDING_COLOR : loyaltyCard.headerColor);
|
||||
iconBitmap = createAdaptiveBitmap(iconBitmap, Utils.getHeaderColor(context, loyaltyCard));
|
||||
}
|
||||
|
||||
IconCompat icon = IconCompat.createWithAdaptiveBitmap(iconBitmap);
|
||||
|
||||
@@ -50,6 +50,8 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
@@ -58,6 +60,8 @@ import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
@@ -76,6 +80,8 @@ public class Utils {
|
||||
public static final int CARD_IMAGE_FROM_FILE_BACK = 9;
|
||||
public static final int CARD_IMAGE_FROM_FILE_ICON = 10;
|
||||
|
||||
public static final String CARD_IMAGE_FILENAME_REGEX = "^(card_)(\\d+)(_(?:front|back|icon)\\.png)$";
|
||||
|
||||
static final double LUMINANCE_MIDPOINT = 0.5;
|
||||
|
||||
static final int BITMAP_SIZE_SMALL = 512;
|
||||
@@ -380,6 +386,31 @@ public class Utils {
|
||||
return cardImageFileNameBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a card image filename (string) with the ID replaced according to the map if the input is a valid card image filename (string), otherwise null.
|
||||
*
|
||||
* @param fileName e.g. "card_1_front.png"
|
||||
* @param idMap e.g. Map.of(1, 2)
|
||||
* @return String e.g. "card_2_front.png"
|
||||
*/
|
||||
static public String getRenamedCardImageFileName(final String fileName, final Map<Integer, Integer> idMap) {
|
||||
Pattern pattern = Pattern.compile(CARD_IMAGE_FILENAME_REGEX);
|
||||
Matcher matcher = pattern.matcher(fileName);
|
||||
if (matcher.matches()) {
|
||||
StringBuilder cardImageFileNameBuilder = new StringBuilder();
|
||||
cardImageFileNameBuilder.append(matcher.group(1));
|
||||
try {
|
||||
int id = Integer.parseInt(matcher.group(2));
|
||||
cardImageFileNameBuilder.append(idMap.getOrDefault(id, id));
|
||||
} catch (NumberFormatException _e) {
|
||||
return null;
|
||||
}
|
||||
cardImageFileNameBuilder.append(matcher.group(3));
|
||||
return cardImageFileNameBuilder.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static public void saveCardImage(Context context, Bitmap bitmap, String fileName) throws FileNotFoundException {
|
||||
if (bitmap == null) {
|
||||
context.deleteFile(fileName);
|
||||
@@ -481,6 +512,18 @@ public class Utils {
|
||||
return new File(context.getCacheDir() + "/" + name);
|
||||
}
|
||||
|
||||
public static File copyToTempFile(Context context, InputStream input, String name) throws IOException {
|
||||
File file = createTempFile(context, name);
|
||||
try (input; FileOutputStream out = new FileOutputStream(file)) {
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
while ((len = input.read(buf)) != -1) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
public static String saveTempImage(Context context, Bitmap in, String name, Bitmap.CompressFormat format) {
|
||||
File image = createTempFile(context, name);
|
||||
try (FileOutputStream out = new FileOutputStream(image)) {
|
||||
@@ -560,16 +603,6 @@ public class Utils {
|
||||
activity.findViewById(android.R.id.content).setBackgroundColor(typedValue.data);
|
||||
}
|
||||
|
||||
public static void updateMenuCardDetailsButtonState(MenuItem item, boolean currentlyExpanded) {
|
||||
if (currentlyExpanded) {
|
||||
item.setIcon(R.drawable.ic_baseline_unfold_less_24);
|
||||
item.setTitle(R.string.action_hide_details);
|
||||
} else {
|
||||
item.setIcon(R.drawable.ic_baseline_unfold_more_24);
|
||||
item.setTitle(R.string.action_show_details);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHeaderColorFromImage(Bitmap image, int fallback) {
|
||||
if (image == null) {
|
||||
return fallback;
|
||||
@@ -603,8 +636,7 @@ public class Utils {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static void setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, ImageView backgroundOrIcon, TextView textWhenNoImage) {
|
||||
Bitmap icon = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
|
||||
public static void setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage) {
|
||||
if (icon != null) {
|
||||
Log.d("onResume", "setting icon image");
|
||||
textWhenNoImage.setVisibility(View.GONE);
|
||||
@@ -614,7 +646,7 @@ public class Utils {
|
||||
} else {
|
||||
textWhenNoImage.setVisibility(View.VISIBLE);
|
||||
|
||||
int headerColor = loyaltyCard.headerColor != null ? loyaltyCard.headerColor : LetterBitmap.getDefaultColor(context, loyaltyCard.store);
|
||||
int headerColor = getHeaderColor(context, loyaltyCard);
|
||||
|
||||
backgroundOrIcon.setImageBitmap(null);
|
||||
backgroundOrIcon.setBackgroundColor(headerColor);
|
||||
@@ -637,4 +669,35 @@ public class Utils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHeaderColor(Context context, LoyaltyCard loyaltyCard) {
|
||||
return loyaltyCard.headerColor != null ? loyaltyCard.headerColor : LetterBitmap.getDefaultColor(context, loyaltyCard.store);
|
||||
}
|
||||
|
||||
public static String checksum(InputStream input) throws IOException {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
while ((len = input.read(buf)) != -1) {
|
||||
md.update(buf, 0, len);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : md.digest()) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException _e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean equals(final Object a, final Object b) {
|
||||
if (a == null && b == null) {
|
||||
return true;
|
||||
} else if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package protect.card_locker.contentprovider;
|
||||
|
||||
import static protect.card_locker.DBHelper.LoyaltyCardDbIds;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import protect.card_locker.BuildConfig;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class CardsContentProvider extends ContentProvider {
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".contentprovider.cards";
|
||||
|
||||
public static class Version {
|
||||
public static final String MAJOR_COLUMN = "major";
|
||||
public static final String MINOR_COLUMN = "minor";
|
||||
public static final int MAJOR = 1;
|
||||
public static final int MINOR = 0;
|
||||
}
|
||||
|
||||
private static final int URI_VERSION = 0;
|
||||
private static final int URI_CARDS = 1;
|
||||
private static final int URI_GROUPS = 2;
|
||||
private static final int URI_CARD_GROUPS = 3;
|
||||
|
||||
private static final String[] CARDS_DEFAULT_PROJECTION = new String[]{
|
||||
LoyaltyCardDbIds.ID,
|
||||
LoyaltyCardDbIds.STORE,
|
||||
LoyaltyCardDbIds.VALID_FROM,
|
||||
LoyaltyCardDbIds.EXPIRY,
|
||||
LoyaltyCardDbIds.BALANCE,
|
||||
LoyaltyCardDbIds.BALANCE_TYPE,
|
||||
LoyaltyCardDbIds.NOTE,
|
||||
LoyaltyCardDbIds.HEADER_COLOR,
|
||||
LoyaltyCardDbIds.CARD_ID,
|
||||
LoyaltyCardDbIds.BARCODE_ID,
|
||||
LoyaltyCardDbIds.BARCODE_TYPE,
|
||||
LoyaltyCardDbIds.STAR_STATUS,
|
||||
LoyaltyCardDbIds.LAST_USED,
|
||||
LoyaltyCardDbIds.ARCHIVE_STATUS,
|
||||
};
|
||||
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH) {{
|
||||
addURI(AUTHORITY, "version", URI_VERSION);
|
||||
addURI(AUTHORITY, "cards", URI_CARDS);
|
||||
addURI(AUTHORITY, "groups", URI_GROUPS);
|
||||
addURI(AUTHORITY, "card_groups", URI_CARD_GROUPS);
|
||||
}};
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri,
|
||||
@Nullable final String[] projection,
|
||||
@Nullable final String selection,
|
||||
@Nullable final String[] selectionArgs,
|
||||
@Nullable final String sortOrder) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// Disable the content provider on SDK < 23 since it grants dangerous
|
||||
// permissions at install-time
|
||||
Log.w(TAG, "Content provider read is only available for SDK >= 23");
|
||||
return null;
|
||||
}
|
||||
|
||||
final Settings settings = new Settings(getContext());
|
||||
if (!settings.getAllowContentProviderRead()) {
|
||||
Log.w(TAG, "Content provider read is disabled");
|
||||
return null;
|
||||
}
|
||||
|
||||
final String table;
|
||||
String[] updatedProjection = projection;
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case URI_VERSION:
|
||||
return queryVersion();
|
||||
case URI_CARDS:
|
||||
table = DBHelper.LoyaltyCardDbIds.TABLE;
|
||||
// Restrict columns to the default projection (omit internal columns such as zoom level)
|
||||
if (projection == null) {
|
||||
updatedProjection = CARDS_DEFAULT_PROJECTION;
|
||||
} else {
|
||||
final Set<String> defaultProjection = new HashSet<>(Arrays.asList(CARDS_DEFAULT_PROJECTION));
|
||||
updatedProjection = Arrays.stream(projection).filter(defaultProjection::contains).toArray(String[]::new);
|
||||
}
|
||||
break;
|
||||
case URI_GROUPS:
|
||||
table = DBHelper.LoyaltyCardDbGroups.TABLE;
|
||||
break;
|
||||
case URI_CARD_GROUPS:
|
||||
table = DBHelper.LoyaltyCardDbIdsGroups.TABLE;
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Unrecognized URI " + uri);
|
||||
return null;
|
||||
}
|
||||
|
||||
final DBHelper dbHelper = new DBHelper(getContext());
|
||||
final SQLiteDatabase database = dbHelper.getReadableDatabase();
|
||||
|
||||
return database.query(
|
||||
table,
|
||||
updatedProjection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
);
|
||||
}
|
||||
|
||||
private Cursor queryVersion() {
|
||||
final String[] columns = new String[]{Version.MAJOR_COLUMN, Version.MINOR_COLUMN};
|
||||
final MatrixCursor matrixCursor = new MatrixCursor(columns);
|
||||
matrixCursor.addRow(new Object[]{Version.MAJOR, Version.MINOR});
|
||||
|
||||
return matrixCursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull final Uri uri) {
|
||||
// MIME types are not relevant (for now at least)
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull final Uri uri,
|
||||
@Nullable final ContentValues values) {
|
||||
// This content provider is read-only for now, so we always return null
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull final Uri uri,
|
||||
@Nullable final String selection,
|
||||
@Nullable final String[] selectionArgs) {
|
||||
// This content provider is read-only for now, so we always return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull final Uri uri,
|
||||
@Nullable final ContentValues values,
|
||||
@Nullable final String selection,
|
||||
@Nullable final String[] selectionArgs) {
|
||||
// This content provider is read-only for now, so we always return 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import org.apache.commons.csv.CSVRecord;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -22,12 +24,17 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.ImageLocationType;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
import protect.card_locker.Utils;
|
||||
import protect.card_locker.ZipUtils;
|
||||
|
||||
@@ -39,24 +46,42 @@ import protect.card_locker.ZipUtils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class CatimaImporter implements Importer {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, InterruptedException {
|
||||
InputStream bufferedInputStream = new BufferedInputStream(input);
|
||||
bufferedInputStream.mark(100);
|
||||
public static class ImportedData {
|
||||
public final List<LoyaltyCard> cards;
|
||||
public final List<String> groups;
|
||||
public final List<Map.Entry<Integer, String>> cardGroups;
|
||||
|
||||
ImportedData(final List<LoyaltyCard> cards, final List<String> groups, final List<Map.Entry<Integer, String>> cardGroups) {
|
||||
this.cards = cards;
|
||||
this.groups = groups;
|
||||
this.cardGroups = cardGroups;
|
||||
}
|
||||
}
|
||||
|
||||
public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, InterruptedException {
|
||||
// Pass #1: get hashes and parse CSV
|
||||
InputStream input1 = new FileInputStream(inputFile);
|
||||
InputStream bufferedInputStream1 = new BufferedInputStream(input1);
|
||||
bufferedInputStream1.mark(100);
|
||||
ZipInputStream zipInputStream1 = new ZipInputStream(bufferedInputStream1, password);
|
||||
|
||||
// First, check if this is a zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(bufferedInputStream, password);
|
||||
|
||||
boolean isZipFile = false;
|
||||
|
||||
LocalFileHeader localFileHeader;
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
Map<String, String> imageChecksums = new HashMap<>();
|
||||
ImportedData importedData = null;
|
||||
|
||||
while ((localFileHeader = zipInputStream1.getNextEntry()) != null) {
|
||||
isZipFile = true;
|
||||
|
||||
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
|
||||
if (fileName.equals("catima.csv")) {
|
||||
importCSV(context, database, zipInputStream);
|
||||
importedData = importCSV(zipInputStream1);
|
||||
} else if (fileName.endsWith(".png")) {
|
||||
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream), fileName);
|
||||
if (!fileName.matches(Utils.CARD_IMAGE_FILENAME_REGEX)) {
|
||||
throw new FormatException("Unexpected PNG file in import: " + fileName);
|
||||
}
|
||||
imageChecksums.put(fileName, Utils.checksum(zipInputStream1));
|
||||
} else {
|
||||
throw new FormatException("Unexpected file in import: " + fileName);
|
||||
}
|
||||
@@ -64,35 +89,110 @@ public class CatimaImporter implements Importer {
|
||||
|
||||
if (!isZipFile) {
|
||||
// This is not a zip file, try importing as bare CSV
|
||||
bufferedInputStream.reset();
|
||||
importCSV(context, database, bufferedInputStream);
|
||||
bufferedInputStream1.reset();
|
||||
importedData = importCSV(bufferedInputStream1);
|
||||
}
|
||||
|
||||
input.close();
|
||||
input1.close();
|
||||
|
||||
if (importedData == null) {
|
||||
throw new FormatException("No imported data");
|
||||
}
|
||||
|
||||
Map<Integer, Integer> idMap = saveAndDeduplicate(context, database, importedData, imageChecksums);
|
||||
|
||||
if (isZipFile) {
|
||||
// Pass #2: save images
|
||||
InputStream input2 = new FileInputStream(inputFile);
|
||||
InputStream bufferedInputStream2 = new BufferedInputStream(input2);
|
||||
ZipInputStream zipInputStream2 = new ZipInputStream(bufferedInputStream2, password);
|
||||
|
||||
while ((localFileHeader = zipInputStream2.getNextEntry()) != null) {
|
||||
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
|
||||
if (fileName.endsWith(".png")) {
|
||||
String newFileName = Utils.getRenamedCardImageFileName(fileName, idMap);
|
||||
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream2), newFileName);
|
||||
}
|
||||
}
|
||||
|
||||
input2.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void importCSV(Context context, SQLiteDatabase database, InputStream input) throws IOException, FormatException, InterruptedException {
|
||||
public Map<Integer, Integer> saveAndDeduplicate(Context context, SQLiteDatabase database, final ImportedData data, final Map<String, String> imageChecksums) throws IOException {
|
||||
Map<Integer, Integer> idMap = new HashMap<>();
|
||||
Set<String> existingImages = DBHelper.imageFiles(context, database);
|
||||
|
||||
for (LoyaltyCard card : data.cards) {
|
||||
LoyaltyCard existing = DBHelper.getLoyaltyCard(database, card.id);
|
||||
if (existing == null) {
|
||||
DBHelper.insertLoyaltyCard(database, card.id, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
|
||||
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
|
||||
} else if (!isDuplicate(context, existing, card, existingImages, imageChecksums)) {
|
||||
long newId = DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
|
||||
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
|
||||
idMap.put(card.id, (int) newId);
|
||||
}
|
||||
}
|
||||
|
||||
for (String group : data.groups) {
|
||||
DBHelper.insertGroup(database, group);
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, String> entry : data.cardGroups) {
|
||||
int cardId = idMap.getOrDefault(entry.getKey(), entry.getKey());
|
||||
String groupId = entry.getValue();
|
||||
// For existing & newly imported cards, add the groups from the import to the internal state
|
||||
List<Group> cardGroups = DBHelper.getLoyaltyCardGroups(database, cardId);
|
||||
cardGroups.add(DBHelper.getGroup(database, groupId));
|
||||
DBHelper.setLoyaltyCardGroups(database, cardId, cardGroups);
|
||||
}
|
||||
|
||||
return idMap;
|
||||
}
|
||||
|
||||
public boolean isDuplicate(Context context, final LoyaltyCard existing, final LoyaltyCard card, final Set<String> existingImages, final Map<String, String> imageChecksums) throws IOException {
|
||||
if (!LoyaltyCard.isDuplicate(existing, card)) {
|
||||
return false;
|
||||
}
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
String name = Utils.getCardImageFileName(existing.id, imageLocationType);
|
||||
boolean exists = existingImages.contains(name);
|
||||
if (exists != imageChecksums.containsKey(name)) {
|
||||
return false;
|
||||
}
|
||||
if (exists) {
|
||||
File file = Utils.retrieveCardImageAsFile(context, name);
|
||||
if (!imageChecksums.get(name).equals(Utils.checksum(new FileInputStream(file)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ImportedData importCSV(InputStream input) throws IOException, FormatException, InterruptedException {
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
int version = parseVersion(bufferedReader);
|
||||
switch (version) {
|
||||
case 1:
|
||||
parseV1(database, bufferedReader);
|
||||
break;
|
||||
return parseV1(bufferedReader);
|
||||
case 2:
|
||||
parseV2(context, database, bufferedReader);
|
||||
break;
|
||||
return parseV2(bufferedReader);
|
||||
default:
|
||||
throw new FormatException(String.format("No code to parse version %s", version));
|
||||
}
|
||||
}
|
||||
|
||||
public void parseV1(SQLiteDatabase database, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
public ImportedData parseV1(BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
ImportedData data = new ImportedData(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
||||
final CSVParser parser = new CSVParser(input, CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
try {
|
||||
for (CSVRecord record : parser) {
|
||||
importLoyaltyCard(database, record);
|
||||
LoyaltyCard card = importLoyaltyCard(record);
|
||||
data.cards.add(card);
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -103,9 +203,15 @@ public class CatimaImporter implements Importer {
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void parseV2(Context context, SQLiteDatabase database, BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
public ImportedData parseV2(BufferedReader input) throws IOException, FormatException, InterruptedException {
|
||||
List<LoyaltyCard> cards = new ArrayList<>();
|
||||
List<String> groups = new ArrayList<>();
|
||||
List<Map.Entry<Integer, String>> cardGroups = new ArrayList<>();
|
||||
|
||||
int part = 0;
|
||||
StringBuilder stringPart = new StringBuilder();
|
||||
|
||||
@@ -123,7 +229,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 1:
|
||||
try {
|
||||
parseV2Groups(database, stringPart.toString());
|
||||
groups = parseV2Groups(stringPart.toString());
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -131,7 +237,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 2:
|
||||
try {
|
||||
parseV2Cards(context, database, stringPart.toString());
|
||||
cards = parseV2Cards(stringPart.toString());
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -139,7 +245,7 @@ public class CatimaImporter implements Importer {
|
||||
break;
|
||||
case 3:
|
||||
try {
|
||||
parseV2CardGroups(database, stringPart.toString());
|
||||
cardGroups = parseV2CardGroups(stringPart.toString());
|
||||
sectionParsed = true;
|
||||
} catch (FormatException e) {
|
||||
// We may have a multiline field, try again
|
||||
@@ -166,9 +272,11 @@ public class CatimaImporter implements Importer {
|
||||
} catch (FormatException e) {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
}
|
||||
|
||||
return new ImportedData(cards, groups, cardGroups);
|
||||
}
|
||||
|
||||
public void parseV2Groups(SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public List<String> parseV2Groups(String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse groups
|
||||
final CSVParser groupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -188,12 +296,15 @@ public class CatimaImporter implements Importer {
|
||||
groupParser.close();
|
||||
}
|
||||
|
||||
List<String> groups = new ArrayList<>();
|
||||
for (CSVRecord record : records) {
|
||||
importGroup(database, record);
|
||||
String group = importGroup(record);
|
||||
groups.add(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void parseV2Cards(Context context, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public List<LoyaltyCard> parseV2Cards(String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse cards
|
||||
final CSVParser cardParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -213,12 +324,15 @@ public class CatimaImporter implements Importer {
|
||||
cardParser.close();
|
||||
}
|
||||
|
||||
List<LoyaltyCard> cards = new ArrayList<>();
|
||||
for (CSVRecord record : records) {
|
||||
importLoyaltyCard(database, record);
|
||||
LoyaltyCard card = importLoyaltyCard(record);
|
||||
cards.add(card);
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
public void parseV2CardGroups(SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException {
|
||||
public List<Map.Entry<Integer, String>> parseV2CardGroups(String data) throws IOException, FormatException, InterruptedException {
|
||||
// Parse card group mappings
|
||||
final CSVParser cardGroupParser = new CSVParser(new StringReader(data), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -238,9 +352,12 @@ public class CatimaImporter implements Importer {
|
||||
cardGroupParser.close();
|
||||
}
|
||||
|
||||
List<Map.Entry<Integer, String>> cardGroups = new ArrayList<>();
|
||||
for (CSVRecord record : records) {
|
||||
importCardGroupMapping(database, record);
|
||||
Map.Entry<Integer, String> entry = importCardGroupMapping(record);
|
||||
cardGroups.add(entry);
|
||||
}
|
||||
return cardGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,8 +393,7 @@ public class CatimaImporter implements Importer {
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(SQLiteDatabase database, CSVRecord record)
|
||||
throws FormatException {
|
||||
private LoyaltyCard importLoyaltyCard(CSVRecord record) throws FormatException {
|
||||
int id = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIds.ID, record);
|
||||
|
||||
String store = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.STORE, record, "");
|
||||
@@ -374,28 +490,28 @@ public class CatimaImporter implements Importer {
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
|
||||
DBHelper.insertLoyaltyCard(database, id, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed, archiveStatus);
|
||||
return new LoyaltyCard(id, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, archiveStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single group into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importGroup(SQLiteDatabase database, CSVRecord record) throws FormatException {
|
||||
private String importGroup(CSVRecord record) throws FormatException {
|
||||
String id = CSVHelpers.extractString(DBHelper.LoyaltyCardDbGroups.ID, record, null);
|
||||
|
||||
if (id == null) {
|
||||
throw new FormatException("Group has no ID: " + record);
|
||||
}
|
||||
|
||||
DBHelper.insertGroup(database, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single card to group mapping into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importCardGroupMapping(SQLiteDatabase database, CSVRecord record) throws FormatException {
|
||||
private Map.Entry<Integer, String> importCardGroupMapping(CSVRecord record) throws FormatException {
|
||||
int cardId = CSVHelpers.extractInt(DBHelper.LoyaltyCardDbIdsGroups.cardID, record);
|
||||
String groupId = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIdsGroups.groupID, record, null);
|
||||
|
||||
@@ -403,8 +519,6 @@ public class CatimaImporter implements Importer {
|
||||
throw new FormatException("Group has no ID: " + record);
|
||||
}
|
||||
|
||||
List<Group> cardGroups = DBHelper.getLoyaltyCardGroups(database, cardId);
|
||||
cardGroups.add(DBHelper.getGroup(database, groupId));
|
||||
DBHelper.setLoyaltyCardGroups(database, cardId, cardGroups);
|
||||
return Map.entry(cardId, groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,21 @@ import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
@@ -31,7 +36,16 @@ import protect.card_locker.Utils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class FidmeImporter implements Importer {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
public static class ImportedData {
|
||||
public final List<LoyaltyCard> cards;
|
||||
|
||||
ImportedData(final List<LoyaltyCard> cards) {
|
||||
this.cards = cards;
|
||||
}
|
||||
}
|
||||
|
||||
public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
InputStream input = new FileInputStream(inputFile);
|
||||
// We actually retrieve a .zip file
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
|
||||
@@ -54,10 +68,14 @@ public class FidmeImporter implements Importer {
|
||||
}
|
||||
|
||||
final CSVParser fidmeParser = new CSVParser(new StringReader(loyaltyCards.toString()), CSVFormat.RFC4180.builder().setDelimiter(';').setHeader().build());
|
||||
ImportedData importedData = new ImportedData(new ArrayList<>());
|
||||
|
||||
try {
|
||||
for (CSVRecord record : fidmeParser) {
|
||||
importLoyaltyCard(context, database, record);
|
||||
LoyaltyCard card = importLoyaltyCard(context, record);
|
||||
if (card != null) {
|
||||
importedData.cards.add(card);
|
||||
}
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException();
|
||||
@@ -70,14 +88,16 @@ public class FidmeImporter implements Importer {
|
||||
}
|
||||
|
||||
zipInputStream.close();
|
||||
input.close();
|
||||
|
||||
saveAndDeduplicate(database, importedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single loyalty card into the database using the given
|
||||
* session.
|
||||
*/
|
||||
private void importLoyaltyCard(Context context, SQLiteDatabase database, CSVRecord record)
|
||||
throws FormatException {
|
||||
private LoyaltyCard importLoyaltyCard(Context context, CSVRecord record) throws FormatException {
|
||||
// A loyalty card export from Fidme contains the following fields:
|
||||
// Retailer (store name)
|
||||
// Program (program name)
|
||||
@@ -113,7 +133,7 @@ public class FidmeImporter implements Importer {
|
||||
// Fidme deletes the card id if a card is expired
|
||||
// Because Catima considers the card id a required field, we ignore these expired cards
|
||||
// https://github.com/CatimaLoyalty/Android/issues/1005
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Sadly, Fidme exports don't contain the card type
|
||||
@@ -128,6 +148,17 @@ public class FidmeImporter implements Importer {
|
||||
|
||||
// TODO: Front and back image
|
||||
|
||||
DBHelper.insertLoyaltyCard(database, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, starStatus, null,archiveStatus);
|
||||
// use -1 for the ID, it will be ignored when inserting the card into the DB
|
||||
return new LoyaltyCard(-1, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, starStatus, Utils.getUnixTime(), DBHelper.DEFAULT_ZOOM_LEVEL, archiveStatus);
|
||||
}
|
||||
|
||||
public void saveAndDeduplicate(SQLiteDatabase database, final ImportedData data) {
|
||||
// This format does not have IDs that can cause conflicts
|
||||
// Proper deduplication for all formats will be implemented later
|
||||
for (LoyaltyCard card : data.cards) {
|
||||
// Do not use card.id which is set to -1
|
||||
DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
|
||||
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
|
||||
import protect.card_locker.FormatException;
|
||||
@@ -23,5 +23,5 @@ public interface Importer {
|
||||
* @throws IOException
|
||||
* @throws FormatException
|
||||
*/
|
||||
void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, InterruptedException, JSONException, ParseException;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,15 @@ import android.util.Log;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
public class MultiFormatImporter {
|
||||
private static final String TAG = "Catima";
|
||||
private static final String TEMP_ZIP_NAME = MultiFormatImporter.class.getSimpleName() + ".zip";
|
||||
|
||||
/**
|
||||
* Attempts to import data from the input stream of the
|
||||
@@ -42,23 +47,33 @@ public class MultiFormatImporter {
|
||||
|
||||
String error = null;
|
||||
if (importer != null) {
|
||||
database.beginTransaction();
|
||||
File inputFile;
|
||||
try {
|
||||
importer.importData(context, database, input, password);
|
||||
database.setTransactionSuccessful();
|
||||
return new ImportExportResult(ImportExportResultType.Success);
|
||||
} catch (ZipException e) {
|
||||
if (e.getType().equals(ZipException.Type.WRONG_PASSWORD)) {
|
||||
return new ImportExportResult(ImportExportResultType.BadPassword);
|
||||
} else {
|
||||
inputFile = Utils.copyToTempFile(context, input, TEMP_ZIP_NAME);
|
||||
database.beginTransaction();
|
||||
try {
|
||||
importer.importData(context, database, inputFile, password);
|
||||
database.setTransactionSuccessful();
|
||||
return new ImportExportResult(ImportExportResultType.Success);
|
||||
} catch (ZipException e) {
|
||||
if (e.getType().equals(ZipException.Type.WRONG_PASSWORD)) {
|
||||
return new ImportExportResult(ImportExportResultType.BadPassword);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
error = e.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
error = e.toString();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
if (!inputFile.delete()) {
|
||||
Log.w(TAG, "Failed to delete temporary ZIP file (should not be a problem) " + inputFile);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to import data", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to copy ZIP file", e);
|
||||
error = e.toString();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
} else {
|
||||
error = "Unsupported data format imported: " + format.name();
|
||||
|
||||
@@ -13,21 +13,29 @@ import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.ImageLocationType;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
import protect.card_locker.ZipUtils;
|
||||
@@ -40,11 +48,30 @@ import protect.card_locker.ZipUtils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class StocardImporter implements Importer {
|
||||
public static class ZIPData {
|
||||
public final HashMap<String, HashMap<String, Object>> loyaltyCardHashMap;
|
||||
public final HashMap<String, HashMap<String, Object>> providers;
|
||||
|
||||
ZIPData(final HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, final HashMap<String, HashMap<String, Object>> providers) {
|
||||
this.loyaltyCardHashMap = loyaltyCardHashMap;
|
||||
this.providers = providers;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImportedData {
|
||||
public final List<LoyaltyCard> cards;
|
||||
public final Map<Integer, Map<ImageLocationType, Bitmap>> images;
|
||||
|
||||
ImportedData(final List<LoyaltyCard> cards, final Map<Integer, Map<ImageLocationType, Bitmap>> images) {
|
||||
this.cards = cards;
|
||||
this.images = images;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = new HashMap<>();
|
||||
HashMap<String, HashMap<String, Object>> providers = new HashMap<>();
|
||||
public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
ZIPData zipData = new ZIPData(new HashMap<>(), new HashMap<>());
|
||||
|
||||
final CSVParser parser = new CSVParser(new InputStreamReader(context.getResources().openRawResource(R.raw.stocard_stores), StandardCharsets.UTF_8), CSVFormat.RFC4180.builder().setHeader().build());
|
||||
|
||||
@@ -54,7 +81,7 @@ public class StocardImporter implements Importer {
|
||||
recordData.put("name", record.get("name"));
|
||||
recordData.put("barcodeFormat", record.get("barcodeFormat"));
|
||||
|
||||
providers.put(record.get("_id"), recordData);
|
||||
zipData.providers.put(record.get("_id"), recordData);
|
||||
}
|
||||
|
||||
parser.close();
|
||||
@@ -62,7 +89,23 @@ public class StocardImporter implements Importer {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
}
|
||||
|
||||
InputStream input = new FileInputStream(inputFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
zipData = importZIP(zipInputStream, zipData);
|
||||
zipInputStream.close();
|
||||
input.close();
|
||||
|
||||
if (zipData.loyaltyCardHashMap.keySet().size() == 0) {
|
||||
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
|
||||
}
|
||||
|
||||
ImportedData importedData = importLoyaltyCardHashMap(context, zipData);
|
||||
saveAndDeduplicate(context, database, importedData);
|
||||
}
|
||||
|
||||
public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, JSONException {
|
||||
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = zipData.loyaltyCardHashMap;
|
||||
HashMap<String, HashMap<String, Object>> providers = zipData.providers;
|
||||
|
||||
String[] providersFileName = null;
|
||||
String[] customProvidersBaseName = null;
|
||||
@@ -142,6 +185,15 @@ public class StocardImporter implements Importer {
|
||||
jsonObject.getString("input_id")
|
||||
);
|
||||
|
||||
if (jsonObject.has("input_provider_name")) {
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"store",
|
||||
jsonObject.getString("input_provider_name")
|
||||
);
|
||||
}
|
||||
|
||||
// Provider ID can be either custom or not, extract whatever version is relevant
|
||||
String customProviderPrefix = "/users/" + nameParts[1] + "/loyalty-card-custom-providers/";
|
||||
String providerId = jsonObject
|
||||
@@ -176,14 +228,28 @@ public class StocardImporter implements Importer {
|
||||
ZipUtils.readJSON(zipInputStream)
|
||||
.getString("content")
|
||||
);
|
||||
} else if (fileName.endsWith("/images/front.png")) {
|
||||
} else if (fileName.endsWith("usage-statistics/content.json")) {
|
||||
JSONArray usages = ZipUtils.readJSON(zipInputStream).getJSONArray("usages");
|
||||
if (usages.length() > 0) {
|
||||
JSONObject lastUsedObject = usages.getJSONObject(usages.length() - 1);
|
||||
String lastUsedString = lastUsedObject.getJSONObject("time").getString("value");
|
||||
long timeStamp = Instant.parse(lastUsedString).getEpochSecond();
|
||||
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"lastUsed",
|
||||
timeStamp
|
||||
);
|
||||
}
|
||||
} else if (fileName.endsWith("/images/front.png") || fileName.endsWith("/images/front/front.jpg")) {
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
"frontImage",
|
||||
ZipUtils.readImage(zipInputStream)
|
||||
);
|
||||
} else if (fileName.endsWith("/images/back.png")) {
|
||||
} else if (fileName.endsWith("/images/back.png") || fileName.endsWith("/images/back/back.jpg")) {
|
||||
loyaltyCardHashMap = appendToHashMap(
|
||||
loyaltyCardHashMap,
|
||||
cardName,
|
||||
@@ -194,11 +260,14 @@ public class StocardImporter implements Importer {
|
||||
}
|
||||
}
|
||||
|
||||
if (loyaltyCardHashMap.keySet().size() == 0) {
|
||||
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
|
||||
}
|
||||
return new ZIPData(loyaltyCardHashMap, providers);
|
||||
}
|
||||
|
||||
for (HashMap<String, Object> loyaltyCardData : loyaltyCardHashMap.values()) {
|
||||
public ImportedData importLoyaltyCardHashMap(Context context, final ZIPData zipData) {
|
||||
ImportedData importedData = new ImportedData(new ArrayList<>(), new HashMap<>());
|
||||
int tempID = 0;
|
||||
|
||||
for (Map<String, Object> loyaltyCardData : zipData.loyaltyCardHashMap.values()) {
|
||||
String providerId = (String) loyaltyCardData.get("_providerId");
|
||||
|
||||
if (providerId == null) {
|
||||
@@ -206,9 +275,16 @@ public class StocardImporter implements Importer {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashMap<String, Object> providerData = providers.get(providerId);
|
||||
HashMap<String, Object> providerData = zipData.providers.get(providerId);
|
||||
|
||||
// Read store from card, if not available (old export), fall back to providerData
|
||||
String store;
|
||||
if (loyaltyCardData.containsKey("store")) {
|
||||
store = (String) loyaltyCardData.get("store");
|
||||
} else {
|
||||
store = providerData != null ? providerData.get("name").toString() : providerId;
|
||||
}
|
||||
|
||||
String store = providerData != null ? providerData.get("name").toString() : providerId;
|
||||
String note = (String) Utils.mapGetOrDefault(loyaltyCardData, "note", "");
|
||||
String cardId = (String) loyaltyCardData.get("cardId");
|
||||
String barcodeTypeString = (String) Utils.mapGetOrDefault(loyaltyCardData, "barcodeType", providerData != null ? providerData.get("barcodeFormat") : null);
|
||||
@@ -230,21 +306,46 @@ public class StocardImporter implements Importer {
|
||||
headerColor = Utils.getHeaderColorFromImage(cardIcon, headerColor);
|
||||
}
|
||||
|
||||
long loyaltyCardInternalId = DBHelper.insertLoyaltyCard(database, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, 0, null,0);
|
||||
long lastUsed;
|
||||
if (loyaltyCardData.containsKey("lastUsed")) {
|
||||
lastUsed = (long) loyaltyCardData.get("lastUsed");
|
||||
} else {
|
||||
lastUsed = Utils.getUnixTime();
|
||||
}
|
||||
|
||||
LoyaltyCard card = new LoyaltyCard(tempID, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, 0, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, 0);
|
||||
importedData.cards.add(card);
|
||||
|
||||
Map<ImageLocationType, Bitmap> images = new HashMap<>();
|
||||
|
||||
if (cardIcon != null) {
|
||||
Utils.saveCardImage(context, cardIcon, (int) loyaltyCardInternalId, ImageLocationType.icon);
|
||||
images.put(ImageLocationType.icon, cardIcon);
|
||||
}
|
||||
|
||||
if (loyaltyCardData.containsKey("frontImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("frontImage"), (int) loyaltyCardInternalId, ImageLocationType.front);
|
||||
images.put(ImageLocationType.front, (Bitmap) loyaltyCardData.get("frontImage"));
|
||||
}
|
||||
if (loyaltyCardData.containsKey("backImage")) {
|
||||
Utils.saveCardImage(context, (Bitmap) loyaltyCardData.get("backImage"), (int) loyaltyCardInternalId, ImageLocationType.back);
|
||||
images.put(ImageLocationType.back, (Bitmap) loyaltyCardData.get("backImage"));
|
||||
}
|
||||
|
||||
importedData.images.put(tempID, images);
|
||||
tempID++;
|
||||
}
|
||||
|
||||
zipInputStream.close();
|
||||
return importedData;
|
||||
}
|
||||
|
||||
public void saveAndDeduplicate(Context context, SQLiteDatabase database, final ImportedData data) throws IOException {
|
||||
// This format does not have IDs that can cause conflicts
|
||||
// Proper deduplication for all formats will be implemented later
|
||||
for (LoyaltyCard card : data.cards) {
|
||||
// card.id is temporary and only used to index the images Map
|
||||
long id = DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
|
||||
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
|
||||
for (Map.Entry<ImageLocationType, Bitmap> entry : data.images.get(card.id).entrySet()) {
|
||||
Utils.saveCardImage(context, entry.getValue(), (int) id, entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startsWith(String[] full, String[] start, int minExtraLength) {
|
||||
@@ -272,4 +373,4 @@ public class StocardImporter implements Importer {
|
||||
|
||||
return loyaltyCardHashMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -19,13 +21,16 @@ import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.FormatException;
|
||||
import protect.card_locker.LoyaltyCard;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
/**
|
||||
@@ -36,7 +41,16 @@ import protect.card_locker.Utils;
|
||||
* A header is expected for the each table showing the names of the columns.
|
||||
*/
|
||||
public class VoucherVaultImporter implements Importer {
|
||||
public void importData(Context context, SQLiteDatabase database, InputStream input, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
public static class ImportedData {
|
||||
public final List<LoyaltyCard> cards;
|
||||
|
||||
ImportedData(final List<LoyaltyCard> cards) {
|
||||
this.cards = cards;
|
||||
}
|
||||
}
|
||||
|
||||
public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, JSONException, ParseException {
|
||||
InputStream input = new FileInputStream(inputFile);
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -46,6 +60,16 @@ public class VoucherVaultImporter implements Importer {
|
||||
}
|
||||
JSONArray jsonArray = new JSONArray(sb.toString());
|
||||
|
||||
bufferedReader.close();
|
||||
input.close();
|
||||
|
||||
ImportedData importedData = importJSON(jsonArray);
|
||||
saveAndDeduplicate(database, importedData);
|
||||
}
|
||||
|
||||
public ImportedData importJSON(JSONArray jsonArray) throws FormatException, JSONException, ParseException {
|
||||
ImportedData importedData = new ImportedData(new ArrayList<>());
|
||||
|
||||
// See https://github.com/tim-smart/vouchervault/issues/4#issuecomment-788226503 for more info
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject jsonCard = jsonArray.getJSONObject(i);
|
||||
@@ -126,9 +150,20 @@ public class VoucherVaultImporter implements Importer {
|
||||
throw new FormatException("Unknown colour type found: " + colorFromJSON);
|
||||
}
|
||||
|
||||
DBHelper.insertLoyaltyCard(database, store, "", null, expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime(),0);
|
||||
// use -1 for the ID, it will be ignored when inserting the card into the DB
|
||||
importedData.cards.add(new LoyaltyCard(-1, store, "", null, expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime(), DBHelper.DEFAULT_ZOOM_LEVEL, 0));
|
||||
}
|
||||
|
||||
bufferedReader.close();
|
||||
return importedData;
|
||||
}
|
||||
|
||||
public void saveAndDeduplicate(SQLiteDatabase database, final ImportedData data) {
|
||||
// This format does not have IDs that can cause conflicts
|
||||
// Proper deduplication for all formats will be implemented later
|
||||
for (LoyaltyCard card : data.cards) {
|
||||
// Do not use card.id which is set to -1
|
||||
DBHelper.insertLoyaltyCard(database, card.store, card.note, card.validFrom, card.expiry, card.balance, card.balanceType,
|
||||
card.cardId, card.barcodeId, card.barcodeType, card.headerColor, card.starStatus, card.lastUsed, card.archiveStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,10 @@ public class Settings {
|
||||
return getBoolean(R.string.settings_key_disable_lockscreen_while_viewing_card, true);
|
||||
}
|
||||
|
||||
public boolean getAllowContentProviderRead() {
|
||||
return getBoolean(R.string.settings_key_allow_content_provider_read, true);
|
||||
}
|
||||
|
||||
public boolean getOledDark() {
|
||||
return getBoolean(R.string.settings_key_oled_dark, false);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package protect.card_locker.preferences;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
@@ -15,7 +16,6 @@ import java.util.Locale;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
@@ -98,23 +98,8 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
// Load the preferences from an XML resource
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
// Show pretty names
|
||||
ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale));
|
||||
assert localePreference != null;
|
||||
CharSequence[] entryValues = localePreference.getEntryValues();
|
||||
List<CharSequence> entries = new ArrayList<>();
|
||||
for (CharSequence entry : entryValues) {
|
||||
if (entry.length() == 0) {
|
||||
entries.add(getResources().getString(R.string.settings_system_locale));
|
||||
} else {
|
||||
Locale entryLocale = Utils.stringToLocale(entry.toString());
|
||||
entries.add(entryLocale.getDisplayName(entryLocale));
|
||||
}
|
||||
}
|
||||
|
||||
localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length]));
|
||||
|
||||
Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
|
||||
// Show pretty names and summaries
|
||||
ListPreference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
|
||||
assert themePreference != null;
|
||||
themePreference.setOnPreferenceChangeListener((preference, o) -> {
|
||||
if (o.toString().equals(getResources().getString(R.string.settings_key_light_theme))) {
|
||||
@@ -128,10 +113,16 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
return true;
|
||||
});
|
||||
|
||||
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
ListPreference themeColorPreference = findPreference(getResources().getString(R.string.setting_key_theme_color));
|
||||
assert themeColorPreference != null;
|
||||
themeColorPreference.setOnPreferenceChangeListener((preference, o) -> {
|
||||
refreshActivity(true);
|
||||
return true;
|
||||
});
|
||||
if (!DynamicColors.isDynamicColorAvailable()) {
|
||||
themeColorPreference.setEntryValues(R.array.color_values_no_dynamic);
|
||||
themeColorPreference.setEntries(R.array.color_value_strings_no_dynamic);
|
||||
}
|
||||
|
||||
Preference oledDarkPreference = findPreference(getResources().getString(R.string.settings_key_oled_dark));
|
||||
assert oledDarkPreference != null;
|
||||
@@ -140,16 +131,29 @@ public class SettingsActivity extends CatimaAppCompatActivity {
|
||||
return true;
|
||||
});
|
||||
|
||||
ListPreference colorPreference = findPreference(getResources().getString(R.string.setting_key_theme_color));
|
||||
assert colorPreference != null;
|
||||
colorPreference.setOnPreferenceChangeListener((preference, o) -> {
|
||||
ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale));
|
||||
assert localePreference != null;
|
||||
CharSequence[] entryValues = localePreference.getEntryValues();
|
||||
List<CharSequence> entries = new ArrayList<>();
|
||||
for (CharSequence entry : entryValues) {
|
||||
if (entry.length() == 0) {
|
||||
entries.add(getResources().getString(R.string.settings_system_locale));
|
||||
} else {
|
||||
Locale entryLocale = Utils.stringToLocale(entry.toString());
|
||||
entries.add(entryLocale.getDisplayName(entryLocale));
|
||||
}
|
||||
}
|
||||
localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length]));
|
||||
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
refreshActivity(true);
|
||||
return true;
|
||||
});
|
||||
if (!DynamicColors.isDynamicColorAvailable()) {
|
||||
colorPreference.setEntryValues(R.array.color_values_no_dynamic);
|
||||
colorPreference.setEntries(R.array.color_value_strings_no_dynamic);
|
||||
}
|
||||
|
||||
// Disable content provider on SDK < 23 since dangerous permissions
|
||||
// are granted at install-time
|
||||
Preference contentProviderReadPreference = findPreference(getResources().getString(R.string.settings_key_allow_content_provider_read));
|
||||
assert contentProviderReadPreference != null;
|
||||
contentProviderReadPreference.setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
|
||||
}
|
||||
|
||||
private void refreshActivity(boolean reloadMain) {
|
||||
|
||||
5
app/src/main/res/drawable/baseline_visibility_24.xml
Normal file
5
app/src/main/res/drawable/baseline_visibility_24.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
||||
</vector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,14l5,-5 5,5z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
</vector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||
</vector>
|
||||
@@ -128,11 +128,10 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_sub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/translate_platform"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/translate_main"/>
|
||||
@@ -169,11 +168,10 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/license_sub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="2dp"
|
||||
android:text="@string/app_license"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/license_main"/>
|
||||
|
||||
@@ -37,36 +37,48 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageButton
|
||||
<Button
|
||||
android:id="@+id/moveUp"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
style="?attr/materialIconButtonFilledStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_up_24"
|
||||
app:icon="@drawable/ic_baseline_keyboard_arrow_up_24"
|
||||
app:iconGravity="textStart"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:contentDescription="@string/moveUp"/>
|
||||
|
||||
<ImageButton
|
||||
<Button
|
||||
android:id="@+id/moveDown"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
style="?attr/materialIconButtonFilledStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_baseline_arrow_drop_down_24"
|
||||
app:icon="@drawable/ic_baseline_keyboard_arrow_down_24"
|
||||
app:iconGravity="textStart"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:contentDescription="@string/moveDown"/>
|
||||
|
||||
<ImageButton
|
||||
<Button
|
||||
android:id="@+id/edit"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
style="?attr/materialIconButtonFilledStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_mode_edit_white_24dp"
|
||||
app:icon="@drawable/ic_mode_edit_white_24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:contentDescription="@string/edit"/>
|
||||
|
||||
<ImageButton
|
||||
<Button
|
||||
android:id="@+id/delete"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
style="?attr/materialIconButtonFilledStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:srcCompat="@drawable/ic_delete_white_24dp"
|
||||
app:icon="@drawable/ic_delete_white_24dp"
|
||||
app:iconGravity="textStart"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:contentDescription="@string/delete"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="?attr/materialCardViewElevatedStyle"
|
||||
android:id="@+id/row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp">
|
||||
android:layout_margin="5dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -18,7 +19,7 @@
|
||||
android:outlineProvider="none"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:strokeWidth="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/note"
|
||||
app:layout_constraintBottom_toTopOf="@+id/store"
|
||||
app:layout_constraintDimensionRatio="85.6f:53.98f"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
@@ -129,6 +130,22 @@
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/store"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icon_layout"
|
||||
app:layout_constraintBottom_toTopOf="@+id/note"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Example store"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="match_parent"
|
||||
@@ -137,10 +154,9 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icon_layout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/store"
|
||||
app:layout_constraintBottom_toTopOf="@+id/info_divider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -165,11 +181,11 @@
|
||||
android:id="@+id/balance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_payments_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:visibility="gone"
|
||||
@@ -184,11 +200,11 @@
|
||||
android:id="@+id/validFrom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_valid_from_24dp"
|
||||
android:drawablePadding="4dp"
|
||||
android:visibility="gone"
|
||||
@@ -203,11 +219,11 @@
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:drawableLeftCompat="@drawable/ic_baseline_access_time_24"
|
||||
android:drawablePadding="4dp"
|
||||
android:visibility="gone"
|
||||
|
||||
@@ -83,7 +83,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp">
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<!-- We don't use these buttons for Talkback -->
|
||||
<ImageButton
|
||||
@@ -101,6 +102,7 @@
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/main_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="8dp"
|
||||
@@ -125,6 +127,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -166,7 +170,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fullscreen_button_minimize"
|
||||
android:layout_marginBottom="50dp"
|
||||
android:layout_marginBottom="25dp"
|
||||
android:layout_marginStart="15.0dip"
|
||||
android:layout_marginEnd="15.0dip">
|
||||
|
||||
@@ -193,7 +197,7 @@
|
||||
android:src="@drawable/ic_baseline_expand_more_24"
|
||||
android:tooltipText="@string/moveDown"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="50dp"
|
||||
android:layout_marginBottom="25dp"
|
||||
android:background="@android:color/transparent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -210,53 +214,70 @@
|
||||
app:contentInsetEnd="0dp"
|
||||
app:fabAlignmentMode="center">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_previous_button"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="left"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_left_24"
|
||||
android:tooltipText="@string/previousCard"
|
||||
android:visibility="gone" />
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_info_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_info_24"
|
||||
android:tooltipText="@string/showMoreInfo"
|
||||
android:visibility="gone" />
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_previous_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_left_24"
|
||||
android:tooltipText="@string/previousCard"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_next_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="right"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_right_24"
|
||||
android:tooltipText="@string/nextCard"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="locale">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_update_balance_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:background="@android:color/transparent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:src="@drawable/ic_baseline_shopping_cart_24"
|
||||
android:tooltipText="@string/updateBalance"
|
||||
android:visibility="gone" />
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_info_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_info_24"
|
||||
android:tooltipText="@string/showMoreInfo"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_update_balance_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_shopping_cart_24"
|
||||
android:tooltipText="@string/updateBalance"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/bottom_app_bar_next_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_baseline_chevron_right_24"
|
||||
android:tooltipText="@string/nextCard"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBox_reverse"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:paddingStart="20dp"
|
||||
android:text="@string/reverse"
|
||||
android:textSize="19sp"/>
|
||||
android:textSize="16sp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
app:showAsAction="always|collapseActionView"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/action_unfold"
|
||||
android:title="@string/action_hide_details"
|
||||
android:icon="@drawable/ic_baseline_unfold_less_24"
|
||||
android:id="@+id/action_shown_details"
|
||||
android:title="@string/action_show_details"
|
||||
android:icon="@drawable/baseline_visibility_24"
|
||||
app:showAsAction="always"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_unfold"
|
||||
android:title="@string/action_hide_details"
|
||||
android:icon="@drawable/ic_baseline_unfold_less_24"
|
||||
android:id="@+id/action_shown_details"
|
||||
android:title="@string/action_show_details"
|
||||
android:icon="@drawable/baseline_visibility_24"
|
||||
app:showAsAction="always"
|
||||
android:visible="true"/>
|
||||
</menu>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
app:showAsAction="always|collapseActionView"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/action_unfold"
|
||||
android:title="@string/action_hide_details"
|
||||
android:icon="@drawable/ic_baseline_unfold_less_24"
|
||||
android:id="@+id/action_shown_details"
|
||||
android:title="@string/action_show_details"
|
||||
android:icon="@drawable/baseline_visibility_24"
|
||||
app:showAsAction="always"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
|
||||
@@ -7,53 +7,58 @@ Heimen Stoffels
|
||||
Oğuz Ersen
|
||||
Katharine Chui
|
||||
mondstern
|
||||
IllusiveMan196
|
||||
StoyanDimitrov
|
||||
Altonss
|
||||
SlavekB
|
||||
StoyanDimitrov
|
||||
IllusiveMan196
|
||||
FC Stegerman
|
||||
Altonss
|
||||
Michael Moroni
|
||||
Gediminas Murauskas
|
||||
Petr Novák
|
||||
Joel A
|
||||
Taco
|
||||
laralem
|
||||
Taco
|
||||
pfaffenrodt
|
||||
Nyatsuki
|
||||
gallegonovato
|
||||
Nyatsuki
|
||||
HudobniVolk
|
||||
Samantaz Fox
|
||||
Eric
|
||||
arno-github
|
||||
Ankit Tiwari
|
||||
Sergio Paredes
|
||||
Clxff H3r4ld0
|
||||
Aayush Gupta
|
||||
Balázs Meskó
|
||||
huuhaa
|
||||
Projjal Moitra
|
||||
Quentin PAGÈS
|
||||
Giovanni Donisi
|
||||
Alexander Ivanov
|
||||
arshbeerSingh
|
||||
Denis Shilin
|
||||
Freddo espresso
|
||||
Projjal Moitra
|
||||
Silvério Santos
|
||||
Miha Frangež
|
||||
Eryk Michalak
|
||||
Arnis Jaundžeikars
|
||||
Kefir2105
|
||||
Dan
|
||||
sr093906
|
||||
Giovanni Donisi
|
||||
mdvhimself
|
||||
Jiri Grönroos
|
||||
Katarzyna
|
||||
echo r"0xX4H" | rev
|
||||
Magnitudee
|
||||
Olivia (Zoe)
|
||||
betsythefc
|
||||
waffshappen
|
||||
(Cliff Heraldo)
|
||||
Robin
|
||||
Eric
|
||||
ati3
|
||||
Balázs Meskó
|
||||
enolp
|
||||
Evgeniy Khramov
|
||||
Jane Kong
|
||||
Jiri Grönroos
|
||||
Jean Mareilles
|
||||
José Rebelo
|
||||
K. Herbert
|
||||
Lisa A.
|
||||
Mawuena M. KODZO A.
|
||||
@@ -62,8 +67,9 @@ Still Hsu
|
||||
String E. Fighter
|
||||
Tapu
|
||||
Yurical
|
||||
Eryk Michalak
|
||||
rr-vesp
|
||||
yangyangdaji
|
||||
丛林意志
|
||||
alajemba-vik
|
||||
/usr/local/ΕΨΗΕΛΩΝ
|
||||
Adolfo Jayme-Barrientos
|
||||
@@ -72,23 +78,32 @@ KovalevArtem
|
||||
Artem M.
|
||||
Astrohops1
|
||||
BMN
|
||||
balaraz
|
||||
BootVirtual
|
||||
Bottan Hermawan
|
||||
zChiip
|
||||
Clonewayx
|
||||
D. Domig
|
||||
Diego
|
||||
Eudes-alencar
|
||||
Fede Pujol
|
||||
FineFindus
|
||||
francescbassas
|
||||
Jason Li
|
||||
Jean-Luc Tibaux
|
||||
Jesse Davids
|
||||
Kis Dominik
|
||||
Lukas Grassauer
|
||||
Luna Jernberg
|
||||
Marnick L'Eau
|
||||
Michalis
|
||||
Michał
|
||||
Milo Ivir
|
||||
Mohamed A. Salah
|
||||
the7thNightmare
|
||||
Rishi Agarwal
|
||||
Rosdyana Kusuma
|
||||
Sabri Ünal
|
||||
umoenks
|
||||
Simon Rusinov
|
||||
Siriusmart
|
||||
@@ -98,16 +113,21 @@ Thomas Bertels
|
||||
Thomas Cruveilher
|
||||
Tian Jiale
|
||||
Tong Liu
|
||||
Tymofii Lytvynenko
|
||||
Wanath
|
||||
Runner
|
||||
ce i moa
|
||||
enescan201
|
||||
Frablock
|
||||
inesre
|
||||
lgasp
|
||||
notlin4
|
||||
phlostically
|
||||
pokeghost
|
||||
sal0max
|
||||
Ágata Leuck
|
||||
BmBKun
|
||||
NamHyeonjeong
|
||||
Aditya Das
|
||||
asier123123131
|
||||
Kevin Sicong Jiang
|
||||
@@ -119,7 +139,8 @@ Airat
|
||||
Alexander
|
||||
sNiXx
|
||||
Angela Enogieru
|
||||
AnimeshChatterjee1
|
||||
Animesh Chatterjee
|
||||
Artūras Kalenda
|
||||
Ashish Yadav
|
||||
Aya Elsaadany
|
||||
Biren
|
||||
@@ -128,45 +149,60 @@ Brage Nesteby Reitan
|
||||
Cap Amr Karam
|
||||
Carlo Maria Cuoghi Barbagli
|
||||
CherryMonster222
|
||||
Colgrave
|
||||
Csaba
|
||||
Mylou53
|
||||
danieluhrinyi
|
||||
Daniele Tricoli
|
||||
Kasina Dheeraj
|
||||
Donno
|
||||
Erik Spjelkavik
|
||||
Flav
|
||||
Franciszek Stefan
|
||||
Gael Caraballo
|
||||
Giacomo Alessandroni
|
||||
Grzegorz
|
||||
gneiss15
|
||||
Hamustra Scans
|
||||
helzubair
|
||||
HowITsDone
|
||||
Hubert Maciejewicz
|
||||
Izzy
|
||||
Jacek
|
||||
Jacopo Gennaro Esposito
|
||||
Jean Mareilles
|
||||
Jean-Baptiste
|
||||
Kung-chih
|
||||
Karvjorm
|
||||
polar
|
||||
Kamborio
|
||||
krkk
|
||||
Laura Ferraz
|
||||
Lucas da Costa
|
||||
almir992
|
||||
Manan Jhaveri
|
||||
Marco
|
||||
BRBsoup
|
||||
Mateo Gomez
|
||||
Mattia
|
||||
Md. Al-Amin
|
||||
Michael Gangolf
|
||||
Milan Šalka
|
||||
3DN1M
|
||||
Minecraft boom
|
||||
Mobashir Raihan
|
||||
Moi Toi
|
||||
OPADILOP
|
||||
DivideEtImpera
|
||||
Nicolas
|
||||
Nosnahc
|
||||
pa4k
|
||||
pbeckmann
|
||||
Peer Beckmann
|
||||
vandman
|
||||
Piotr Strebski
|
||||
Piotr Zet
|
||||
Poorva Patidar
|
||||
Quang Trung
|
||||
Quang Nguyen
|
||||
Ratnesh
|
||||
Reza
|
||||
@@ -177,20 +213,28 @@ SKULD
|
||||
Salem Malus
|
||||
Samarth Asthan
|
||||
Shailendra Maurya
|
||||
SilverFS
|
||||
Simone Dotto
|
||||
Subhashish Anand
|
||||
Subhradeep Bera
|
||||
Swayam Khare
|
||||
SziaTomi
|
||||
Mehedi Hasan
|
||||
Tim Trek
|
||||
Titas Pažereckas
|
||||
atakujonc
|
||||
tkraljevic
|
||||
Tony C
|
||||
Tymofii Lytvynenko
|
||||
Vancha March
|
||||
tyap-lyap-ivprod
|
||||
Waldemar Stoczkowski
|
||||
Wiktor Kwapisiewicz
|
||||
Yevgeny M
|
||||
Yusril A
|
||||
Ziad OUALHADJ
|
||||
ahmed-awad26
|
||||
Avik Kundu
|
||||
ayuyydev
|
||||
diksha-2911
|
||||
gbonaspetti
|
||||
gittyboy-cell
|
||||
@@ -206,11 +250,12 @@ sergio
|
||||
080502
|
||||
Marcus
|
||||
techwebpd
|
||||
tjw123hh
|
||||
Truestorybaby
|
||||
tygyh
|
||||
unstartdev
|
||||
wmilan 17
|
||||
يوسف لطفي
|
||||
luoyang3
|
||||
元气
|
||||
JaeBeom An
|
||||
JungHee Lee
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# stocard_stores.csv
|
||||
|
||||
stocard_stores.csv was created by extracting /data/data/de.stocard/de.stocard.stocard/databases/stores on a rooted devices and running the following command over it:
|
||||
|
||||
```
|
||||
sqlite3 -header -csv sync_db "select id,content from synced_resources where collection = '/loyalty-card-providers/'" > stocard_providers.csv
|
||||
while IFS= read -r line; do
|
||||
if [ "$line" = "id,content" ]; then
|
||||
echo "_id,name,barcodeFormat" > stocard_stores.csv
|
||||
else
|
||||
id="$(echo "$line" | cut -d ',' -f1)"
|
||||
name="$(echo "$line" | cut -d ',' -f2- | sed 's/""/"/g' | sed 's/^"//g' | sed 's/"$//g' | jq -r .name)"
|
||||
barcodeFormat="$(echo "$line" | cut -d ',' -f2- | sed 's/""/"/g' | sed 's/^"//g' | sed 's/"$//g' | jq -r .default_barcode_format)"
|
||||
echo "$id,\"$name\",$barcodeFormat" >> stocard_stores.csv
|
||||
fi
|
||||
done < stocard_providers.csv
|
||||
```
|
||||
|
||||
Only used for data portability reasons (ensuring importing works). Do NOT copy this anywhere else or use it for any purpose other than ensuring we can import a GDPR-provided export. We want to make sure this stays under fair use.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,7 +55,6 @@
|
||||
<string name="thumbnailDescription">صورة مصغرة</string>
|
||||
<string name="starImage">نجم مفضل</string>
|
||||
<string name="settings">اعدادات</string>
|
||||
<string name="settings_category_title_ui">واجهة المستخدم</string>
|
||||
<string name="settings_light_theme">فاتح</string>
|
||||
<string name="settings_dark_theme">داكن</string>
|
||||
<string name="settings_card_orientation">اتجاه الباركود</string>
|
||||
@@ -289,4 +288,4 @@
|
||||
<string name="chooseValidFromDate">اختر صالح من التاريخ</string>
|
||||
<string name="validFromSentence">صالح من:<xliff:g>%s</xliff:g></string>
|
||||
<string name="height">الطول:</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
56
app/src/main/res/values-ast/strings.xml
Normal file
56
app/src/main/res/values-ast/strings.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="storeName">Nome</string>
|
||||
<string name="note">Nota</string>
|
||||
<string name="noMatchingGiftCards">Nun hai nengún resultáu. Prueba a camudar la busca.</string>
|
||||
<string name="barcodeType">Tipu de códigu de barres</string>
|
||||
<string name="noBarcode">Ensin códigu de barres</string>
|
||||
<string name="star">Meter en Favoritos</string>
|
||||
<string name="confirm">Confirmar</string>
|
||||
<string name="deleteConfirmation">¿Quies desaniciar esta tarxeta permanentemente\?</string>
|
||||
<string name="importExport">Importar/Esportar</string>
|
||||
<string name="noCardIdError">Nun s\'introduxo nenguna ID</string>
|
||||
<string name="exportFailed">Num se pudo facer la esportación</string>
|
||||
<string name="importFailedTitle">La importación falló</string>
|
||||
<string name="exportOptionExplanation">Los datos escríbense nel llugar qu\'escueyas.</string>
|
||||
<string name="app_license">Software llibre llicenciao cola GPLv3+</string>
|
||||
<string name="action_search">Buscar</string>
|
||||
<string name="action_add">Amestar</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> na seleición</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> na seleición</item>
|
||||
</plurals>
|
||||
<string name="noGiftCards">Calca\'l botón «+» p\'amestar una tarxeta o impórtales dende\'l menú «⋮».</string>
|
||||
<string name="cardId">ID</string>
|
||||
<string name="barcodeNoBarcode">Nun hai nengún códigu de barres</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="delete">Desaniciar</string>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one"></item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<string name="unstar">Quitar de Favoritos</string>
|
||||
<string name="cancel">Encaboxar</string>
|
||||
<string name="importFailed">Nun se pudo facer la importación</string>
|
||||
<string name="noCardExistsError">Nun se pudo atopar esa tarxeta</string>
|
||||
<string name="all">Too</string>
|
||||
<string name="noStoreError">Nun s\'introduxo nengún nome</string>
|
||||
<string name="sendLabel">Unviar…</string>
|
||||
<string name="editCardTitle">Editar la tarxeta</string>
|
||||
<string name="ok">D\'acuerdu</string>
|
||||
<string name="exportFailedTitle">La esportación falló</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Pa escaniar códigos de barres, Catima tien d\'acceder a la cámara. Toca equí pa camudar la configuración de los permisos.</string>
|
||||
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="importing">Importando…</string>
|
||||
<string name="exporting">Esportando…</string>
|
||||
<string name="storageReadPermissionRequired">Esta aición precisa\'l permisu pa lleer l\'almacenamientu…</string>
|
||||
<string name="cameraPermissionRequired">Esta aición precisa l\'accesu a la cámara…</string>
|
||||
<string name="cameraPermissionDeniedTitle">Nun se pudo acceder a la cámara</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_copyright_old">Básase en Loyalty Card Keychain
|
||||
\ncopyright © 2016–2020 Branden Archer</string>
|
||||
<string name="thumbnailDescription">Miniatura</string>
|
||||
<string name="settings">Configuración</string>
|
||||
<string name="settings_theme">Estilu</string>
|
||||
</resources>
|
||||
@@ -12,8 +12,8 @@
|
||||
<string name="star">Добавя към любими</string>
|
||||
<string name="noBarcode">Липсва щрихкод</string>
|
||||
<string name="barcodeNoBarcode">Липсва щрихкод</string>
|
||||
<string name="barcodeType">Вид на щрихкод</string>
|
||||
<string name="cardId">Идентификатор на карта</string>
|
||||
<string name="barcodeType">Вид на щрихкода</string>
|
||||
<string name="cardId">Номер на карта</string>
|
||||
<string name="note">Бележка</string>
|
||||
<string name="storeName">Наименование</string>
|
||||
<string name="noMatchingGiftCards">Няма резултати. Променете критериите за търсене.</string>
|
||||
@@ -35,19 +35,19 @@
|
||||
<string name="photos">Снимки</string>
|
||||
<string name="importOptionApplicationExplanation">Изберете файл на друго приложение.</string>
|
||||
<string name="noCardExistsError">Картата не е намерена</string>
|
||||
<string name="updateBarcodeQuestionText">Идентификаторът е променен. Желаете ли с неговата стойност да бъде променен и щрихкодът\?</string>
|
||||
<string name="updateBarcodeQuestionText">Номерът е променен. Желаете ли същата стойност да бъде приложена и към щрихкода\?</string>
|
||||
<string name="updateBarcodeQuestionTitle">Обновяване на щрихкода\?</string>
|
||||
<string name="noCardIdError">Не е въведен идентификатор</string>
|
||||
<string name="noCardIdError">Не е въведен номер</string>
|
||||
<string name="noCardsMessage">Добавете карта</string>
|
||||
<string name="cardShortcut">Пряк път до карта</string>
|
||||
<string name="addCardTitle">Добавяне на карта</string>
|
||||
<string name="removeImage">Премахване на изображение</string>
|
||||
<string name="takePhoto">Снимане</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Идентификаторите са копирани в междинната памет</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">Номерата са копирани в междинната памет</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Искам да споделя тези карти с вас</string>
|
||||
<string name="wrongValueForBarcodeType">Стойността е невалидна за избрания вид щрихкод</string>
|
||||
<string name="wrongValueForBarcodeType">Неприемлива стойност за избрания вид щрихкод</string>
|
||||
<string name="setBarcodeId">Задаване на стойност</string>
|
||||
<string name="sameAsCardId">Същата като идентификатора</string>
|
||||
<string name="sameAsCardId">Като номера</string>
|
||||
<string name="barcodeId">Стойност на щрихкода</string>
|
||||
<string name="importLoyaltyCardKeychain">Внасяне от Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Изберете файла <i>fidme-export-request-xxxxxx.zip</i>, предварително изнесен от FidMe и ръчно изберете вида на щрихкодовете.
|
||||
@@ -63,7 +63,7 @@
|
||||
<string name="turn_flashlight_off">Изключва светкавицата</string>
|
||||
<string name="turn_flashlight_on">Включва светкавицата</string>
|
||||
<string name="passwordRequired">Въведете паролата</string>
|
||||
<string name="points">Точки</string>
|
||||
<string name="points">точки</string>
|
||||
<string name="currency">Валута</string>
|
||||
<string name="balance">Наличност</string>
|
||||
<string name="errorReadingImage">Изображението е нечетимо</string>
|
||||
@@ -71,11 +71,11 @@
|
||||
<string name="moveBarcodeToTopOfScreen">Премества щрихкода най-горе на екрана</string>
|
||||
<string name="never">Не изтича</string>
|
||||
<string name="chooseExpiryDate">Определена дата</string>
|
||||
<string name="expiryDate">Валидна до</string>
|
||||
<string name="expiryDate">Валидност до</string>
|
||||
<string name="editBarcode">Редактиране на щрихкод</string>
|
||||
<string name="card">Карта</string>
|
||||
<string name="groupsList">Списъци: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Валидна до: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Валидност до: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Изтекла: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balanceSentence">Наличност: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noGroups">Докоснете бутона +, за да добавите списък.</string>
|
||||
@@ -83,19 +83,18 @@
|
||||
<string name="groups">Списъци</string>
|
||||
<string name="enter_group_name">Въведете име на списъка</string>
|
||||
<string name="intent_import_card_from_url_share_text">Искам да споделя тази карта с вас</string>
|
||||
<string name="settings_display_barcode_max_brightness">Увеличаване на яркостта при видим щрихкод</string>
|
||||
<string name="settings_keep_screen_on">Поддържане на екрана включен</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Предотвратяване на заключване на екрана</string>
|
||||
<string name="settings_display_barcode_max_brightness">Максимална яркост при видим щрихкод</string>
|
||||
<string name="settings_keep_screen_on">Без изключване на екрана</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Без заключване на екрана</string>
|
||||
<string name="settings_dark_theme">Тъмна</string>
|
||||
<string name="settings_light_theme">Светла</string>
|
||||
<string name="settings_system_theme">Системна</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_category_title_ui">Потребителски интерфейс</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="starImage">Звезда за любимо</string>
|
||||
<string name="thumbnailDescription">Миниатюра</string>
|
||||
<string name="copy_to_clipboard_toast">Идентификаторът е копиран в междинната памет</string>
|
||||
<string name="enterBarcodeInstructions">Въведете идентификатор и или изберете вида на щрихкода, или докоснете бутона „Липсва щрихкод“.</string>
|
||||
<string name="copy_to_clipboard_toast">Номерът е копиран в междинната памет</string>
|
||||
<string name="enterBarcodeInstructions">Въведете номер и после или изберете вида на щрихкода, или докоснете бутона „Липсва щрихкод“.</string>
|
||||
<string name="selectBarcodeTitle">Избиране на щрихкод</string>
|
||||
<string name="importOptionApplicationButton">Избиране чрез приложение</string>
|
||||
<string name="importing">Внасяне…</string>
|
||||
@@ -113,7 +112,7 @@
|
||||
<string name="scanCardBarcode">Снемане на щрихкод</string>
|
||||
<string name="editCardTitle">Редактиране на карта</string>
|
||||
<string name="share">Споделя</string>
|
||||
<string name="copy_to_clipboard">Копира идентификатора в междинната памет</string>
|
||||
<string name="copy_to_clipboard">Копира номера в междинната памет</string>
|
||||
<string name="ok">Добре</string>
|
||||
<string name="importSuccessful">Данните са внесени</string>
|
||||
<string name="chooseImportType">Внасяне на данни на</string>
|
||||
@@ -196,15 +195,15 @@
|
||||
<string name="and_data_usage">и използване на данни</string>
|
||||
<string name="help_translate_this_app">Помогнете за превода на приложението</string>
|
||||
<string name="exportPasswordHint">Въведете парола</string>
|
||||
<string name="exportPassword">Задаване на парола за защита на изнесеното (по избор)</string>
|
||||
<string name="setIcon">Задаване на пиктограма</string>
|
||||
<string name="exportPassword">Защитете архива с парола (по желание)</string>
|
||||
<string name="setIcon">Задаване на миниатюра</string>
|
||||
<string name="editGroup">Променяне на списъка: <xliff:g>%s</xliff:g></string>
|
||||
<string name="group_name_already_in_use">Има списък с това име</string>
|
||||
<string name="group_updated">Промените са запазени</string>
|
||||
<string name="selectColor">Избиране на цвят</string>
|
||||
<string name="group_name_is_empty">Името на списъка не може да е празно</string>
|
||||
<string name="group_edit">Редактиране на списък</string>
|
||||
<string name="action_show_details">Повече детайли</string>
|
||||
<string name="action_show_details">Подробности под картата</string>
|
||||
<string name="action_hide_details">По-малко детайли</string>
|
||||
<string name="noGiftCardsGroup">Създайте карти и ги зачислите към списък от тук.</string>
|
||||
<string name="translate_platform">в Weblate</string>
|
||||
@@ -212,18 +211,18 @@
|
||||
<string name="starred">Със звезда</string>
|
||||
<string name="showMoreInfo">Показване на информация</string>
|
||||
<string name="options">Настройки</string>
|
||||
<string name="card_ids_copied">Идентификаторите са копирани</string>
|
||||
<string name="card_ids_copied">Номерата са копирани</string>
|
||||
<plurals name="balancePoints">
|
||||
<item quantity="one"><xliff:g>%s</xliff:g> точка</item>
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> точки</item>
|
||||
</plurals>
|
||||
<string name="settings_oled_dark">Чисто черен фон за тъмната тема</string>
|
||||
<string name="settings_oled_dark">Черен фон за тъмната тема</string>
|
||||
<string name="include_if_asking_support">Ако искате да потърсите поддръжка, включете следната информация:</string>
|
||||
<string name="settings_card_orientation">Положение на щрихкода</string>
|
||||
<string name="settings_card_orientation">Завъртане на щрихкода</string>
|
||||
<string name="settings_follow_system_orientation">Според системата</string>
|
||||
<string name="settings_portrait_orientation">Портретно</string>
|
||||
<string name="settings_landscape_orientation">Пейзажно</string>
|
||||
<string name="settings_lock_on_opening_orientation">Използване на положението, използвано при отваряне на картата</string>
|
||||
<string name="settings_portrait_orientation">Портрет</string>
|
||||
<string name="settings_landscape_orientation">Пейзаж</string>
|
||||
<string name="settings_lock_on_opening_orientation">Като при отваряне на картата</string>
|
||||
<string name="duplicateCard">Дублиране</string>
|
||||
<string name="archive">Архивиране</string>
|
||||
<string name="unarchive">Изваждане от архива</string>
|
||||
@@ -256,9 +255,9 @@
|
||||
<string name="importCards">Внасяне на карти</string>
|
||||
<string name="storageReadPermissionRequired">За това действие е необходимо разрешение за четене на хранилището…</string>
|
||||
<string name="cameraPermissionRequired">За това действие е необходимо разрешение за достъп до камерата…</string>
|
||||
<string name="validFromDate">Валидна от</string>
|
||||
<string name="validFromDate">Валидност от</string>
|
||||
<string name="anyDate">Без значение от датата</string>
|
||||
<string name="validFromSentence">Валидна от: <xliff:g>%s</xliff:g></string>
|
||||
<string name="validFromSentence">Валидност от: <xliff:g>%s</xliff:g></string>
|
||||
<string name="chooseValidFromDate">Определена дата</string>
|
||||
<string name="height">Височина:</string>
|
||||
<string name="switchToFrontImage">Показване на предната страна</string>
|
||||
@@ -268,4 +267,20 @@
|
||||
<string name="openBackImageInGalleryApp">Отваряне на изображението на задната страна в приложението галерия</string>
|
||||
<string name="setBarcodeHeight">Задаване на височина на щрихкода</string>
|
||||
<string name="donate">Даряване</string>
|
||||
<string name="icon_header_click_text">Задръжте, за да промените миниатюрата</string>
|
||||
<string name="show_note">Бележка</string>
|
||||
<string name="show_balance">Баланс</string>
|
||||
<string name="show_validity">Валидност</string>
|
||||
<string name="show_name_below_image_thumbnail">Наименование</string>
|
||||
<string name="permissionReadCardsLabel">Четене на карти на Catima</string>
|
||||
<string name="permissionReadCardsDescription">Четене на картите с всички подробности, включително бележки и изображения</string>
|
||||
<string name="settings_allow_content_provider_read_title">Разрешаване на достъп на други приложения до данните</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Необходимо за работата на някои скенери</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Без заключване на екрана при преглед на карта</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Приложенията ще искат разрешение, за да получат достъп</string>
|
||||
<string name="settings_oled_dark_summary">Намалява разхода на батерия от OLED дисплеи</string>
|
||||
<string name="settings_category_title_cards">Карти</string>
|
||||
<string name="settings_category_title_general">Общи</string>
|
||||
<string name="settings_category_title_privacy">Поверителност</string>
|
||||
<string name="settings_keep_screen_on_summary">Спира автоматичното заключване на екрана при преглед на карти</string>
|
||||
</resources>
|
||||
@@ -24,7 +24,6 @@
|
||||
<string name="settings_light_theme">সাদাটে থিম</string>
|
||||
<string name="settings_system_theme">যন্ত্রর থিম</string>
|
||||
<string name="settings_theme">থিম</string>
|
||||
<string name="settings_category_title_ui">বিভাগ শিরোনাম</string>
|
||||
<string name="starImage">তারা ছবি</string>
|
||||
<string name="importCatima">ক্যাতিনা আগম</string>
|
||||
<string name="importLoyaltyCardKeychain">আমদানি লয়্যালটি কার্ড কীচেন</string>
|
||||
@@ -69,7 +68,7 @@
|
||||
<string name="sort_by_expiry">মেয়াদ শেষ করে সাজান</string>
|
||||
<string name="reverse">...উল্টো ক্রমে</string>
|
||||
<string name="sort_by">ক্রমানুসার</string>
|
||||
<string name="noCardExistsError">কার্ডটি পাওয়া যায়নি</string>
|
||||
<string name="noCardExistsError">কার্ডটি খুঁজে পাওয়া গেল না</string>
|
||||
<string name="noStoreError">স্টোরেজ ত্রুটি নেই</string>
|
||||
<string name="card_ids_copied">আইডি কপি করা হয়েছে</string>
|
||||
<string name="noCardsMessage">কোন কার্ড বার্তা নেই</string>
|
||||
@@ -97,10 +96,10 @@
|
||||
<string name="expiryDate">মেয়াদোত্তীর্ণ তারিখ</string>
|
||||
<string name="noBarcodeFound">কোনো বারকোড পাওয়া যায়নি</string>
|
||||
<string name="cameraPermissionRequired">এই কাজটির জন্য ক্যামেরা ব্যবহার করার অনুমতি লাগবে…</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">বারকোড স্ক্যান করার জন্য, কাটিমাকে ফোনের ক্যামেরা ব্যবহার করার অনুমতি দিতে হবে। এইখানে টাচ করে আপনার অনুমতি সেটিংস পালটে নিন।</string>
|
||||
<string name="importOptionApplicationExplanation">আপনার প্রিয় ফাইল ম্যানেজার বা আর যেকোনো অ্যাপ দিয়ে একটি ফাইল খুলুন।</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">মেধাস্বত্ব © ২০১৯-<xliff:g>%d</xliff:g> সিলভিয়া ভান অস</string>
|
||||
<string name="app_license">কপিলেফ্ট করা মুক্ত সফটওয়্যার, জিপিএলের ৩য় এবং তার অধিক সংস্করণে লাইসেন্স করা</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">বারকোড স্ক্যান করার জন্য, Catima কে ক্যামেরাটি ব্যবহার করার অনুমতি দিতে হবে। এইখানে টাচ করে আপনার অনুমতি সেটিংস পালটে নিন।</string>
|
||||
<string name="importOptionApplicationExplanation">একটি ফাইল খোলার জন্য যেকোনো অ্যাপ বা আপনার প্রিয় ফাইল ম্যানেজারটি ব্যবহার করুন।</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">মেধাস্বত্ব © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_license">কপিলেফট দ্বারা রক্ষা করা মুক্ত সফটওয়্যার, লাইসেন্স করা GPLv3+ এর অধীনে</string>
|
||||
<string name="enterBarcodeInstructions">আইডিটি লিখুন আর নয় নিচ থেকে একটি বারকোডের প্রকার বা \"কোনো বারকোড নেই\", নির্বাচন করুন।</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">এই <xliff:g>%d</xliff:g>টি কার্ড কি চিরকালের জন্য মুছে দেবো\?</item>
|
||||
@@ -114,10 +113,10 @@
|
||||
<item quantity="one"><xliff:g>%d</xliff:g>টি কার্ড মুছে ফেলুন</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g>টি কার্ড মুছে ফেলুন</item>
|
||||
</plurals>
|
||||
<string name="cameraPermissionDeniedTitle">ফোনের ক্যামেরা ব্যবহার করা যাচ্ছে না</string>
|
||||
<string name="importOptionFilesystemExplanation">ফোনের স্টোরেজ থেকে নির্দিষ্ট একটি ফাইল আনুন।</string>
|
||||
<string name="cameraPermissionDeniedTitle">ক্যামেরাটি ব্যবহার করা যাচ্ছে না</string>
|
||||
<string name="importOptionFilesystemExplanation">স্টোরেজ থেকে নির্দিষ্ট একটি ফাইল বাছুন।</string>
|
||||
<string name="app_libraries">মুক্ত লাইব্রেরি যেগুলি আমার নয়: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="about_title_fmt"><xliff:g id="app_name">%s</xliff:g>টির ব্যাপারে</string>
|
||||
<string name="about_title_fmt"><xliff:g id="app_name">%s</xliff:g>টির সম্পর্কে</string>
|
||||
<string name="app_resources">মুক্ত সম্পদ যেগুলি আমার নয়: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="thumbnailDescription">থাম্বনেইল</string>
|
||||
<string name="settings_card_orientation">বারকোড অভিমুখ</string>
|
||||
@@ -125,29 +124,29 @@
|
||||
<string name="settings_portrait_orientation">প্রতিকৃতি</string>
|
||||
<string name="barcodeImageDescriptionWithType">ছবি <xliff:g>%s</xliff:g> বারকোড</string>
|
||||
<string name="exportName">রপ্তানি</string>
|
||||
<string name="failedParsingImportUriError">আমদানির ইউআরআই বোঝা যাচ্ছে না</string>
|
||||
<string name="failedParsingImportUriError">আমদানির URI-টি বোঝা যাচ্ছে না</string>
|
||||
<string name="importExport">আমদানি/রপ্তানি</string>
|
||||
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
||||
<string name="exportFailed">বার করা যাচ্ছে না</string>
|
||||
<string name="exportFailed">রপ্তানি করা যাচ্ছে না</string>
|
||||
<string name="copy_to_clipboard_toast">আইডি ক্লিপবোর্ডে নকল করা হল</string>
|
||||
<string name="noCardIdError">কোনো আইডি দওয়া হয়নি</string>
|
||||
<string name="importExportHelp">নিজের তথ্য অন্য কোথাও সংরক্ষণ করে রাখলে পরে সেটা অন্য ফোনে আবার নিয়ে নাওয়া যাই।</string>
|
||||
<string name="importFailed">আনা যাচ্ছে না</string>
|
||||
<string name="importExportHelp">নিজের ডেটা অন্য কোথাও সংরক্ষণ করে রাখলে পরে সেটা অন্য ডিভাইসে সরিয়ে নিতে পারবেন।</string>
|
||||
<string name="importFailed">আমদানি করা গেল না</string>
|
||||
<string name="noGiftCardsGroup">কিছু কার্ড বানান আর এই গ্রুপে স্থির করুন।</string>
|
||||
<string name="scanCardBarcode">বারকোড স্ক্যান করুন</string>
|
||||
<string name="importSuccessfulTitle">আনা শেষ</string>
|
||||
<string name="importFailedTitle">আনা ব্যর্থ</string>
|
||||
<string name="exportSuccessfulTitle">বার করা শেষ</string>
|
||||
<string name="exportFailedTitle">বার করা ব্যর্থ</string>
|
||||
<string name="importing">আনা হচ্ছে…</string>
|
||||
<string name="exporting">বার করা হচ্ছে…</string>
|
||||
<string name="importSuccessfulTitle">আমদানি শেষ</string>
|
||||
<string name="importFailedTitle">আমদানি ব্যর্থ</string>
|
||||
<string name="exportSuccessfulTitle">রপ্তানি শেষ</string>
|
||||
<string name="exportFailedTitle">রপ্তানি ব্যর্থ</string>
|
||||
<string name="importing">আমদানি করা হচ্ছে…</string>
|
||||
<string name="exporting">রপ্তানি করা হচ্ছে…</string>
|
||||
<string name="storageReadPermissionRequired">এই কাজটির জন্য ফোনের স্টোরেজ দেখার অনুমতি লাগবে…</string>
|
||||
<string name="exportOptionExplanation">তথ্যটি আপনার পছন্দের জায়গায় রাখা হবে।</string>
|
||||
<string name="importOptionFilesystemTitle">ফোনের স্টোরেজ থেকে আনুন</string>
|
||||
<string name="importOptionFilesystemButton">ফোনের স্টোরেজ থেকে</string>
|
||||
<string name="exportOptionExplanation">ডেটাটি আপনার পছন্দের জায়গায় রাখা হবে।</string>
|
||||
<string name="importOptionFilesystemTitle">স্টোরেজ থেকে আমদানি করুন</string>
|
||||
<string name="importOptionFilesystemButton">স্টোরেজ থেকে</string>
|
||||
<string name="importOptionApplicationTitle">অন্য অ্যাপ ব্যবহার করুন</string>
|
||||
<string name="app_copyright_old">লয়ালটি কার্ড কিচ্যেনের উপর ভিত্তি করে
|
||||
\nমেধাস্বত্ব © ২০১৬-২০২০ ব্রানডেন আর্চার</string>
|
||||
<string name="app_copyright_old">Loyalty Card Keychain এর উপর ভিত্তি করে
|
||||
\nমেধাস্বত্ব © 2016–2020 Branden Archer</string>
|
||||
<string name="selectBarcodeTitle">বারকোড নির্বাচন করুন</string>
|
||||
<string name="settings">সেটিংস</string>
|
||||
<string name="settings_dark_theme">অন্ধকার</string>
|
||||
@@ -260,4 +259,17 @@
|
||||
<string name="updateBalanceTitle">আপনি কত খরচ করেছেন\?</string>
|
||||
<string name="updateBalanceHint">পরিমান লিখুন</string>
|
||||
<string name="currentBalanceSentence">বর্তমান ব্যালেন্স: <xliff:g>%s</xliff:g></string>
|
||||
<string name="show_name_below_image_thumbnail">ছবির থাম্বনেইল এর নিচে নামটি দেখান</string>
|
||||
<string name="show_note">নোট দেখান</string>
|
||||
<string name="show_validity">বৈধতা দেখান</string>
|
||||
<string name="height">উচ্চতা:</string>
|
||||
<string name="switchToBackImage">পিছনের ছবিটিতে সুইচ করুন</string>
|
||||
<string name="switchToFrontImage">সামনের ছবিটিতে সুইচ করুন</string>
|
||||
<string name="switchToBarcode">বারকোডে সুইচ করুন</string>
|
||||
<string name="openFrontImageInGalleryApp">সামনের ছবিটি গ্যালারি অ্যাপে খুলুন</string>
|
||||
<string name="openBackImageInGalleryApp">পিছনের ছবিটি গ্যালারি অ্যাপে খুলুন</string>
|
||||
<string name="setBarcodeHeight">বারকোডের উচ্চতা সেট করুন</string>
|
||||
<string name="icon_header_click_text">দীর্ঘক্ষন টাচ করে থাম্বনেইল এডিট করবেন</string>
|
||||
<string name="show_balance">ব্যালান্স দেখান</string>
|
||||
<string name="donate">দান করুন</string>
|
||||
</resources>
|
||||
@@ -52,4 +52,7 @@
|
||||
</plurals>
|
||||
<string name="deleteTitle">কার্ড মুছুন</string>
|
||||
<string name="noGiftCards">একটি কার্ড যোগ করতে + প্লাস বোতামে ক্লিক করুন বা ⋮ মেনু থেকে আমদানি করুন।</string>
|
||||
<string name="cardShortcut">কার্ড শর্টকাট</string>
|
||||
<string name="noCardsMessage">প্রথমে একটি কার্ড যোগ করুন</string>
|
||||
<string name="card_ids_copied">আইডি কপি করা হয়েছে</string>
|
||||
</resources>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<string name="settings_light_theme">Svjetlo</string>
|
||||
<string name="settings_system_theme">Sistem</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Korisničko okruženje</string>
|
||||
<string name="starImage">Omiljena zvijezda</string>
|
||||
<string name="importCatima">Uvezi iz Catima</string>
|
||||
<string name="importLoyaltyCardKeychain">Uvezi iz Loyalty Card Keychain</string>
|
||||
@@ -111,4 +110,4 @@
|
||||
<string name="importOptionApplicationExplanation">Koristi bilo koju aplikaciju ili Vašu omiljenu aplikaciju da bi otvorili file.</string>
|
||||
<string name="importOptionApplicationButton">Koristi drugu aplikaciju</string>
|
||||
<string name="about">O</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="settings_light_theme">Světlý</string>
|
||||
<string name="settings_system_theme">Podle systému</string>
|
||||
<string name="settings_theme">Vzhled</string>
|
||||
<string name="settings_category_title_ui">Uživatelské rozhraní</string>
|
||||
<string name="settings">Nastavení</string>
|
||||
<string name="card">Karta</string>
|
||||
<string name="balanceSentence">Zůstatek: <xliff:g>%s</xliff:g></string>
|
||||
@@ -203,7 +202,7 @@
|
||||
<string name="exportPassword">Nastavení hesla pro ochranu exportu (volitelné)</string>
|
||||
<string name="exportPasswordHint">Zadejte heslo</string>
|
||||
<string name="selectColor">Vybrat barvu</string>
|
||||
<string name="setIcon">Nastavit ikonu</string>
|
||||
<string name="setIcon">Nastavit miniaturu</string>
|
||||
<string name="group_edit">Upravit skupinu</string>
|
||||
<string name="group_name_already_in_use">Název skupiny je již použitý</string>
|
||||
<string name="group_name_is_empty">Název skupiny nemůže být prázdný</string>
|
||||
@@ -275,4 +274,20 @@
|
||||
<string name="openBackImageInGalleryApp">Otevřít zadní obrázek v galerii</string>
|
||||
<string name="setBarcodeHeight">Nastavit výšku čárového kódu</string>
|
||||
<string name="donate">Přispět</string>
|
||||
<string name="icon_header_click_text">Dlouhým stisknutím miniaturu upravíte</string>
|
||||
<string name="show_name_below_image_thumbnail">Zobrazit název pod obrázkem miniatury</string>
|
||||
<string name="show_note">Zobrazit poznámku</string>
|
||||
<string name="show_validity">Zobrazit platnost</string>
|
||||
<string name="show_balance">Zobrazit zůstatek</string>
|
||||
<string name="permissionReadCardsDescription">Číst vaše karty a všechny jejich podrobnosti, včetně poznámek a obrázků</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Aplikace stále budou muset požádat o povolení k poskytnutí přístupu</string>
|
||||
<string name="permissionReadCardsLabel">Číst karty Catima</string>
|
||||
<string name="settings_allow_content_provider_read_title">Umožnit ostatním aplikacím přístup k mým datům</string>
|
||||
<string name="settings_keep_screen_on_summary">Zakáže časový limit obrazovky při prohlížení karty</string>
|
||||
<string name="settings_oled_dark_summary">Snižuje používání baterie na displejích OLED</string>
|
||||
<string name="settings_category_title_privacy">Soukromí</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Nezbytné pro některé skenery, aby fungovaly</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Zakáže zamčení obrazovky při prohlížení karty</string>
|
||||
<string name="settings_category_title_cards">Karty</string>
|
||||
<string name="settings_category_title_general">Obecné</string>
|
||||
</resources>
|
||||
@@ -48,7 +48,6 @@
|
||||
<string name="settings_dark_theme">Mørk</string>
|
||||
<string name="settings_light_theme">Lys</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Brugergrænseflade</string>
|
||||
<string name="settings">Indstillinger</string>
|
||||
<string name="starImage">Favorit stjerne</string>
|
||||
<string name="thumbnailDescription">Miniaturebillede til kort</string>
|
||||
@@ -89,4 +88,4 @@
|
||||
<string name="moveDown">Bevæger sig nedad</string>
|
||||
<string name="leaveWithoutSaveTitle">Afslut</string>
|
||||
<string name="addManually">Indtast kort-ID manuelt</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
<string name="copy_to_clipboard_toast">ID in die Zwischenablage kopiert</string>
|
||||
<string name="thumbnailDescription">Vorschaubild</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="settings_category_title_ui">Benutzeroberfläche</string>
|
||||
<string name="settings_display_barcode_max_brightness">Displayhelligkeit in der Barcodeansicht erhöhen</string>
|
||||
<string name="exportSuccessful">Daten exportiert</string>
|
||||
<string name="importSuccessful">Daten importiert</string>
|
||||
@@ -204,7 +203,7 @@
|
||||
<string name="editGroup">Gruppe wird bearbeitet: <xliff:g>%s</xliff:g></string>
|
||||
<string name="group_edit">Gruppe bearbeiten</string>
|
||||
<string name="noGiftCardsGroup">Erstelle einige Karten und ordne sie dann hier der Gruppe zu.</string>
|
||||
<string name="setIcon">Symbol einstellen</string>
|
||||
<string name="setIcon">Vorschau setzen</string>
|
||||
<string name="selectColor">Farbe auswählen</string>
|
||||
<string name="action_show_details">Details anzeigen</string>
|
||||
<string name="action_hide_details">Details ausblenden</string>
|
||||
@@ -260,4 +259,28 @@
|
||||
<string name="validFromSentence">Gültig ab: <xliff:g>%s</xliff:g></string>
|
||||
<string name="chooseValidFromDate">Gültig-ab-Datum wählen</string>
|
||||
<string name="anyDate">Beliebiges Datum</string>
|
||||
<string name="icon_header_click_text">Langes Drücken zum Bearbeiten der Vorschau</string>
|
||||
<string name="switchToBarcode">Umschalten auf Barcode</string>
|
||||
<string name="openFrontImageInGalleryApp">Vorderes Bild in der Galerie öffnen</string>
|
||||
<string name="openBackImageInGalleryApp">Hinteres Bild in der Galerie öffnen</string>
|
||||
<string name="height">Höhe:</string>
|
||||
<string name="switchToFrontImage">Zum vorderen Bild wechseln</string>
|
||||
<string name="switchToBackImage">Zum hinteren Bild wechseln</string>
|
||||
<string name="setBarcodeHeight">Barcode-Höhe einstellen</string>
|
||||
<string name="donate">Spenden</string>
|
||||
<string name="show_note">Notiz anzeigen</string>
|
||||
<string name="show_balance">Betrag anzeigen</string>
|
||||
<string name="show_validity">Gültigkeitsdauer anzeigen</string>
|
||||
<string name="show_name_below_image_thumbnail">Namen unter Bildvorschau anzeigen</string>
|
||||
<string name="settings_allow_content_provider_read_title">Anderen Anwendungen den Zugriff auf meine Daten gestatten</string>
|
||||
<string name="permissionReadCardsLabel">Catima-Karten lesen</string>
|
||||
<string name="permissionReadCardsDescription">Lesen Sie Ihre Karten mit allen Details, einschließlich Notizen und Bildern</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Anwendungen müssen weiterhin eine Genehmigung beantragen, um Zugriff zu erhalten</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Erforderlich für das Funktionieren einiger Scanner</string>
|
||||
<string name="settings_keep_screen_on_summary">Deaktiviert die Bildschirmzeitüberschreitung beim Anzeigen einer Karte</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Deaktiviert die Bildschirmsperre während der Anzeige einer Karte</string>
|
||||
<string name="settings_oled_dark_summary">Reduziert den Batterieverbrauch bei OLED-Displays</string>
|
||||
<string name="settings_category_title_cards">Karten</string>
|
||||
<string name="settings_category_title_privacy">Datenschutz</string>
|
||||
<string name="settings_category_title_general">Allgemein</string>
|
||||
</resources>
|
||||
@@ -46,7 +46,6 @@
|
||||
<string name="copy_to_clipboard_toast">Ο κωδικός αντιγράφτηκε στο πρόχειρο</string>
|
||||
<string name="thumbnailDescription">Μικρογραφία</string>
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="settings_category_title_ui">Διεπαφή χρήστη</string>
|
||||
<string name="settings_dark_theme">Σκοτεινό</string>
|
||||
<string name="settings_light_theme">Φωτεινό</string>
|
||||
<string name="settings_system_theme">Σύστημα</string>
|
||||
@@ -254,4 +253,4 @@
|
||||
<string name="newBalanceSentence">Νέο υπόλοιπο: <xliff:g>%s</xliff:g></string>
|
||||
<string name="failedToOpenUrl">Εγκαταστήστε έναν περιηγητή πρώτα</string>
|
||||
<string name="welcome">Καλώς ήρθατε στο Catima</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<string name="noBarcode">Sen strekokodo</string>
|
||||
<string name="barcodeType">Tipo de strekokodo</string>
|
||||
<string name="cardId">Identigilo de karto</string>
|
||||
<string name="settings_category_title_ui">Fasado</string>
|
||||
<string name="settings">Agordoj</string>
|
||||
<string name="selectBarcodeTitle">Elekti strekokodon</string>
|
||||
<string name="debug_version_fmt">Versio: <xliff:g id="version">%s</xliff:g></string>
|
||||
@@ -82,4 +81,9 @@
|
||||
<string name="intent_import_card_from_url_share_text">Mi deziras dividi karto kun vi</string>
|
||||
<string name="exportSuccessful">Karto datumo eksportita</string>
|
||||
<string name="noGroupCards">Ĉi tiu grupo ne enhavas ajnan kartoj</string>
|
||||
</resources>
|
||||
<string name="noGiftCards">Klavu la \"+\" butonon por aldoni karton, aŭ importu uzi menuo \" ⋮\".</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> elektita</item>
|
||||
<item quantity="other"><xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> elektitaj</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
<string name="about_title_fmt">Acerca de <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versión: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="settings">Ajustes</string>
|
||||
<string name="settings_category_title_ui">Interfaz de usuario</string>
|
||||
<string name="settings_display_barcode_max_brightness">Iluminar vista del código de barras</string>
|
||||
<string name="exportSuccessful">Datos exportados</string>
|
||||
<string name="importSuccessful">Datos importados</string>
|
||||
@@ -216,7 +215,7 @@
|
||||
<string name="translate_platform">en Weblate</string>
|
||||
<string name="action_show_details">Mostrar detalles</string>
|
||||
<string name="selectColor">Seleccione el color</string>
|
||||
<string name="setIcon">Establecer icono</string>
|
||||
<string name="setIcon">Establecer una miniatura</string>
|
||||
<string name="sort">Ordenar</string>
|
||||
<string name="sort_by">Ordenar por</string>
|
||||
<string name="rate_this_app">Califica esta aplicación</string>
|
||||
@@ -275,4 +274,20 @@
|
||||
<string name="donate">Donar</string>
|
||||
<string name="switchToBarcode">Cambiar a código de barras</string>
|
||||
<string name="switchToBackImage">Cambiar a imagen trasera</string>
|
||||
<string name="icon_header_click_text">Pulsación larga para editar la miniatura</string>
|
||||
<string name="show_name_below_image_thumbnail">Mostrar el nombre debajo de la miniatura de la imagen</string>
|
||||
<string name="show_note">Mostrar la nota</string>
|
||||
<string name="show_validity">Mostrar la validez</string>
|
||||
<string name="show_balance">Mostrar el saldo</string>
|
||||
<string name="permissionReadCardsLabel">Leer Tarjetas Catima</string>
|
||||
<string name="permissionReadCardsDescription">Lee tus tarjetas y todos sus detalles, incluidas notas e imágenes</string>
|
||||
<string name="settings_allow_content_provider_read_title">Permite a otras aplicaciones acceder a mis datos</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Necesario para que funcione en algunos escáneres</string>
|
||||
<string name="settings_keep_screen_on_summary">Deshabilita el tiempo de espera de la pantalla mientras se ve una tarjeta</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Las aplicaciones todavía tendrán que solicitar permiso para conseguir acceso</string>
|
||||
<string name="settings_oled_dark_summary">Reduce el uso de batería en pantallas OLED</string>
|
||||
<string name="settings_category_title_cards">Tarjetas</string>
|
||||
<string name="settings_category_title_general">General</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Deshabilita el bloqueo de pantalla mientras se ve una tarjeta</string>
|
||||
<string name="settings_category_title_privacy">Privacidad</string>
|
||||
</resources>
|
||||
@@ -72,7 +72,6 @@
|
||||
<string name="settings_light_theme">Vaalea</string>
|
||||
<string name="settings_system_theme">Järjestelmän oletus</string>
|
||||
<string name="settings_theme">Teema</string>
|
||||
<string name="settings_category_title_ui">Käyttöliittymä</string>
|
||||
<string name="settings">Asetukset</string>
|
||||
<string name="starImage">Suosikkitähti</string>
|
||||
<string name="thumbnailDescription">Pienoiskuva</string>
|
||||
@@ -189,7 +188,7 @@
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> pistettä</item>
|
||||
</plurals>
|
||||
<string name="settings_oled_dark">Musta tausta tummalle teemalle</string>
|
||||
<string name="setIcon">Aseta kuvake</string>
|
||||
<string name="setIcon">Aseta pikkukuva</string>
|
||||
<string name="sort_by_name">Nimi</string>
|
||||
<string name="sort_by_most_recently_used">Viimeksi käytetyt</string>
|
||||
<string name="sort_by_expiry">Viimeinen voimassaoloaika</string>
|
||||
@@ -260,4 +259,28 @@
|
||||
<string name="anyDate">Mikä tahansa päivämäärä</string>
|
||||
<string name="chooseValidFromDate">Valitse kelvollinen päivämäärä</string>
|
||||
<string name="validFromSentence">Kelvollinen alkaen: <xliff:g>%s</xliff:g></string>
|
||||
<string name="donate">Lahjoita</string>
|
||||
<string name="permissionReadCardsLabel">Lue Catima-kortteja</string>
|
||||
<string name="permissionReadCardsDescription">Lue korttisi ja kaikki niiden tiedot, mukaan lukien huomautukset ja kuvat</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Sovellusten tulee silti pyytää lupaa saadakseen pääsyn</string>
|
||||
<string name="settings_category_title_privacy">Yksityisyys</string>
|
||||
<string name="height">Korkeus:</string>
|
||||
<string name="switchToFrontImage">Vaihda etukuvaan</string>
|
||||
<string name="switchToBarcode">Vaihda viivakoodiin</string>
|
||||
<string name="openFrontImageInGalleryApp">Avaa etukuva galleriasovelluksessa</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Välttämätön, jotta jotkin skannerit toimivat</string>
|
||||
<string name="settings_keep_screen_on_summary">Poistaa näytön aikakatkaisun korttia katsellessa</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Poistaa näyttölukituksen käytöstä korttia katsellessa</string>
|
||||
<string name="settings_allow_content_provider_read_title">Salli muiden sovellusten käyttää omaa dataa</string>
|
||||
<string name="settings_oled_dark_summary">Vähentää akun käyttöä OLED-näytöillä</string>
|
||||
<string name="switchToBackImage">Vaihda takakuvaan</string>
|
||||
<string name="openBackImageInGalleryApp">Avaa takakuva galleriasovelluksessa</string>
|
||||
<string name="setBarcodeHeight">Aseta viivakoodin korkeus</string>
|
||||
<string name="icon_header_click_text">Pitkä painallus pikkukuvan muokkaamiseksi</string>
|
||||
<string name="show_name_below_image_thumbnail">Näytä nimi pikkukuvan alapuolella</string>
|
||||
<string name="show_note">Näytä huomautus</string>
|
||||
<string name="show_balance">Näytä saldo</string>
|
||||
<string name="show_validity">Näytä kelpoisuus</string>
|
||||
<string name="settings_category_title_cards">Kortit</string>
|
||||
<string name="settings_category_title_general">Yleiset</string>
|
||||
</resources>
|
||||
@@ -46,7 +46,6 @@
|
||||
<string name="copy_to_clipboard_toast">Identifiant copié dans le presse-papiers</string>
|
||||
<string name="thumbnailDescription">Miniature</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="settings_category_title_ui">Interface utilisateur</string>
|
||||
<string name="settings_display_barcode_max_brightness">Augmenter la luminosité du code-barres</string>
|
||||
<string name="exportSuccessful">Données exportées</string>
|
||||
<string name="importSuccessful">Données importées</string>
|
||||
@@ -208,7 +207,7 @@
|
||||
<string name="group_name_already_in_use">Nom de groupe déjà utilisé</string>
|
||||
<string name="group_updated">Groupe mis à jour</string>
|
||||
<string name="group_name_is_empty">Le nom du groupe ne peut pas être vide</string>
|
||||
<string name="setIcon">Définir l’icône</string>
|
||||
<string name="setIcon">Définir la vignette</string>
|
||||
<string name="selectColor">Sélectionnez la couleur</string>
|
||||
<string name="action_show_details">Afficher les détails</string>
|
||||
<string name="action_hide_details">Masquer les détails</string>
|
||||
@@ -275,4 +274,20 @@
|
||||
<string name="openBackImageInGalleryApp">Ouvrir l\'image arrière dans l\'application galerie</string>
|
||||
<string name="setBarcodeHeight">Définir la hauteur du code-barres</string>
|
||||
<string name="donate">Faire un don</string>
|
||||
<string name="icon_header_click_text">Appuyez longuement pour modifier la vignette</string>
|
||||
<string name="show_name_below_image_thumbnail">Afficher le nom sous la vignette de l\'image</string>
|
||||
<string name="show_note">Afficher la note</string>
|
||||
<string name="show_validity">Afficher la validité</string>
|
||||
<string name="show_balance">Afficher le solde</string>
|
||||
<string name="permissionReadCardsLabel">Lire les cartes Catima</string>
|
||||
<string name="permissionReadCardsDescription">Lisez vos cartes et tous ses détails, y compris les notes et les images</string>
|
||||
<string name="settings_allow_content_provider_read_title">Autoriser d\'autres applications à accéder à mes données</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Les applications devront toujours demander une autorisation pour obtenir l\'accès</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Nécessaire au fonctionnement de certains scanneurs</string>
|
||||
<string name="settings_keep_screen_on_summary">Désactive la temporisation de l\'écran lors de la visualisation d\'une carte</string>
|
||||
<string name="settings_oled_dark_summary">Réduit l\'utilisation de la batterie sur les écrans OLED</string>
|
||||
<string name="settings_category_title_cards">Cartes</string>
|
||||
<string name="settings_category_title_general">Généraux</string>
|
||||
<string name="settings_category_title_privacy">Confidentialité</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Désactive le verrouillage de l\'écran pendant la visualisation d\'une carte</string>
|
||||
</resources>
|
||||
@@ -68,7 +68,6 @@
|
||||
<string name="cameraPermissionDeniedTitle">हम कैमरा तक पहुँच नहीं सकते</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">बारकोड स्कैन करने के लिए,को आपके कैमरा का इस्तेमाल करना होगा। इजाज़त कि व्यवस्था (सेटिंग) बदलने के लिए यहाँ दबायें।</string>
|
||||
<string name="importOptionApplicationExplanation">फाइल खोलने के लिए कोई भी ऐप या अपना पसंदिता फाइल मैनेजर का इस्तेमाल करे।</string>
|
||||
<string name="settings_category_title_ui">उपयोक्ता अंतरापृष्ठ (यूजर इंटरफ़ेस)</string>
|
||||
<string name="settings_theme">विषय</string>
|
||||
<string name="barcodeType">बारकोड का प्रकार</string>
|
||||
<string name="barcodeNoBarcode">कोई बारकोड नहीं है</string>
|
||||
@@ -146,4 +145,4 @@
|
||||
<string name="about_title_fmt"><xliff:g id="app_name">%s</xliff:g> के बारे में</string>
|
||||
<string name="debug_version_fmt">संस्करण: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="copy_to_clipboard_toast">आई डी क्लिपबोर्ड पर कॉपी किया गया</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<string name="star">Dodaj u favorite</string>
|
||||
<string name="action_add">Dodaj</string>
|
||||
<string name="storeName">Ime</string>
|
||||
<string name="note">Napomena</string>
|
||||
<string name="noMatchingGiftCards">Nema rezultata. Pokušaj promijeniti pretragu.</string>
|
||||
<string name="note">Bilješka</string>
|
||||
<string name="noMatchingGiftCards">Nema rezultata. Pokušajte promijeniti pretraživanje.</string>
|
||||
<string name="cardId">ID kartice</string>
|
||||
<string name="barcodeType">Vrsta crtičnog koda</string>
|
||||
<string name="barcodeNoBarcode">Ne postoji crtični kod</string>
|
||||
@@ -60,7 +60,6 @@
|
||||
<string name="addManually">Ručno upiši ID</string>
|
||||
<string name="thumbnailDescription">Sličica</string>
|
||||
<string name="starImage">Omiljena zvijezda</string>
|
||||
<string name="settings_category_title_ui">Korisničko sučelje</string>
|
||||
<string name="exportSuccessful">Podaci su izvezeni</string>
|
||||
<string name="settings_keep_screen_on">Ostavi ekran uključen</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Spriječi zaključavanje ekrana</string>
|
||||
@@ -100,7 +99,7 @@
|
||||
<string name="action_hide_details">Sakrij detalje</string>
|
||||
<string name="importCards">Uvezi kartice</string>
|
||||
<string name="selectColor">Odaberi boju</string>
|
||||
<string name="setIcon">Postavi ikonu</string>
|
||||
<string name="setIcon">Postavi sličicu</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_green_theme">Zelena</string>
|
||||
<string name="settings_grey_theme">Siva</string>
|
||||
@@ -147,7 +146,7 @@
|
||||
<string name="noBarcodeFound">Nijedan crtični kod nije pronađen</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="chooseImportType">Uvezi podatke iz</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="app_loyalty_card_keychain">Privjesak za Karticu Vjernosti</string>
|
||||
<string name="parsingBalanceFailed">Čini se da <xliff:g>%s</xliff:g> nije ispravni saldo.</string>
|
||||
<string name="privacy_policy_popup_text">Obavijest o politici privatnosti (obavezna za neke trgovine aplikacija):
|
||||
\n
|
||||
@@ -275,4 +274,9 @@
|
||||
<item quantity="few">Pogledaj arhivu (<xliff:g>%1$d</xliff:g> kartice)</item>
|
||||
<item quantity="other">Pogledaj arhivu (<xliff:g>%1$d</xliff:g> kartica)</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
<string name="icon_header_click_text">Dugim pritiskom uredite sličicu</string>
|
||||
<string name="show_name_below_image_thumbnail">Prikaži ime ispod sličice</string>
|
||||
<string name="show_note">Prikaži bilješku</string>
|
||||
<string name="show_balance">Prikaži saldo</string>
|
||||
<string name="show_validity">Prikaži valjanost</string>
|
||||
</resources>
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
<string name="copy_to_clipboard_toast">Azonosító vágólapra másolva</string>
|
||||
<string name="starImage">Kedvencek csillag</string>
|
||||
<string name="settings">Beállítások</string>
|
||||
<string name="settings_category_title_ui">Felhasználói felület</string>
|
||||
<string name="settings_theme">Téma</string>
|
||||
<string name="settings_system_theme">Rendszer</string>
|
||||
<string name="settings_light_theme">Világos</string>
|
||||
@@ -164,7 +163,7 @@
|
||||
<string name="settings_locale">Nyelv</string>
|
||||
<string name="settings_system_locale">Rendszer</string>
|
||||
<string name="selectColor">Szín kiválasztása</string>
|
||||
<string name="setIcon">Ikon beállítása</string>
|
||||
<string name="setIcon">Miniatűr beállítása</string>
|
||||
<string name="settings_pink_theme">Rózsaszín</string>
|
||||
<string name="settings_magenta_theme">Bíbor</string>
|
||||
<string name="settings_violet_theme">Ibolya</string>
|
||||
@@ -268,4 +267,20 @@
|
||||
<string name="openBackImageInGalleryApp">Hátulnézeti kép megnyitása a galéria alkalmazásban</string>
|
||||
<string name="openFrontImageInGalleryApp">Elölnézeti kép megnyitása a galéria alkalmazásban</string>
|
||||
<string name="setBarcodeHeight">Vonalkód magasságának megadása</string>
|
||||
<string name="icon_header_click_text">Hosszan nyomja meg a miniatűr szerkesztéséhez</string>
|
||||
<string name="show_note">Megjegyzés megjelenítése</string>
|
||||
<string name="show_balance">Egyenleg megjelenítése</string>
|
||||
<string name="permissionReadCardsLabel">Catima kártyák olvasása</string>
|
||||
<string name="permissionReadCardsDescription">Olvassa el a kártyáit és annak minden részletét, beleértve a jegyzeteket és a képeket is</string>
|
||||
<string name="show_validity">Érvényesség megjelenítése</string>
|
||||
<string name="show_name_below_image_thumbnail">Név megjelenítése a kép miniatűrje alatt</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Szükséges egyes szkennerek működéséhez</string>
|
||||
<string name="settings_oled_dark_summary">Csökkenti az OLED kijelzők akkumulátor-használatát</string>
|
||||
<string name="settings_category_title_privacy">Adatvédelem</string>
|
||||
<string name="settings_allow_content_provider_read_title">Engedélyezem más alkalmazások számára az adataimhoz való hozzáférést</string>
|
||||
<string name="settings_category_title_cards">Kártyák</string>
|
||||
<string name="settings_category_title_general">Általános</string>
|
||||
<string name="settings_keep_screen_on_summary">Letiltja a képernyő időtúllépését a kártya megtekintése közben</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Letiltja a képernyő időtúllépését a kártya megjelenítése közben</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Az alkalmazásoknak továbbra is engedélyt kell kérniük a hozzáféréshez</string>
|
||||
</resources>
|
||||
@@ -29,7 +29,6 @@
|
||||
<string name="barcodeNoBarcode">Tidak ada barcode</string>
|
||||
<string name="cancel">Batalkan</string>
|
||||
<string name="importExport">Impor/Ekspor</string>
|
||||
<string name="settings_category_title_ui">Tampilan Pengguna</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="all">Semua</string>
|
||||
<string name="leaveWithoutSaveTitle">Keluar</string>
|
||||
@@ -198,7 +197,7 @@
|
||||
<string name="selectColor">Pilih warna</string>
|
||||
<string name="noGiftCardsGroup">Buat beberapa kartu, kemudian masukkan mereka ke grup di sini.</string>
|
||||
<string name="group_name_already_in_use">Nama grup telah dipakai</string>
|
||||
<string name="setIcon">Atur ikon</string>
|
||||
<string name="setIcon">Atur thumbnail</string>
|
||||
<string name="settings_oled_dark">Latar belakang gelap gulita untuk tema gelap</string>
|
||||
<string name="group_edit">Sunting Grup</string>
|
||||
<string name="group_name_is_empty">Nama grup tidak boleh kosong</string>
|
||||
@@ -250,8 +249,7 @@
|
||||
<string name="cameraPermissionRequired">Berikan izin untuk mengakses kamera anda…</string>
|
||||
<string name="storageReadPermissionRequired">Berikan izin untuk membaca penyimpanan anda…</string>
|
||||
<string name="validFromDate">Valid dari</string>
|
||||
<string name="validFromSentence">Valid dari:
|
||||
\n<xliff:g>%s</xliff:g></string>
|
||||
<string name="validFromSentence">Valid dari: <xliff:g>%s</xliff:g></string>
|
||||
<string name="anyDate">Kapan pun</string>
|
||||
<string name="chooseValidFromDate">Pilih valid dari tanggal</string>
|
||||
<string name="height">Tinggi:</string>
|
||||
@@ -262,4 +260,20 @@
|
||||
<string name="openBackImageInGalleryApp">Buka gambar dibelakang di galeri app</string>
|
||||
<string name="setBarcodeHeight">Atur tinggi kode batang</string>
|
||||
<string name="donate">Donasi</string>
|
||||
<string name="show_validity">Tunjukkan validitas</string>
|
||||
<string name="show_balance">Tunjukkan keseimbangan</string>
|
||||
<string name="icon_header_click_text">Tekan lama untuk mengedit thumbnail</string>
|
||||
<string name="show_name_below_image_thumbnail">Tampilkan nama di bawah thumbnail gambar</string>
|
||||
<string name="show_note">Tampilkan catatan</string>
|
||||
<string name="permissionReadCardsLabel">Baca Kartu Catima</string>
|
||||
<string name="permissionReadCardsDescription">Baca kartu Anda dan semua detailnya, termasuk catatan dan gambar</string>
|
||||
<string name="settings_allow_content_provider_read_title">Izinkan aplikasi lain mengakses data saya</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Aplikasi masih harus meminta izin untuk diberikan akses</string>
|
||||
<string name="settings_keep_screen_on_summary">Menonaktifkan batas waktu layar saat melihat kartu</string>
|
||||
<string name="settings_oled_dark_summary">Mengurangi penggunaan baterai pada layar OLED</string>
|
||||
<string name="settings_category_title_general">Umum</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Diperlukan agar beberapa pemindai dapat berfungsi</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Menonaktifkan kunci layar saat melihat kartu</string>
|
||||
<string name="settings_category_title_cards">Kartu</string>
|
||||
<string name="settings_category_title_privacy">Privasi</string>
|
||||
</resources>
|
||||
@@ -59,7 +59,6 @@
|
||||
<string name="settings_theme">Þema</string>
|
||||
<string name="app_license">Copylefted frítt hugbúnaður, leyfi GPLv3+</string>
|
||||
<string name="noBarcodeFound">Nei strikamerkið var komist</string>
|
||||
<string name="settings_category_title_ui">Notandi tengi</string>
|
||||
<string name="settings_system_theme">Kerfi</string>
|
||||
<string name="settings_dark_theme">Dökk</string>
|
||||
<string name="settings_display_barcode_max_brightness">Bjartari strikamerkið skoða</string>
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
<string name="copy_to_clipboard_toast">Codice copiato negli appunti</string>
|
||||
<string name="thumbnailDescription">Miniatura</string>
|
||||
<string name="settings">Impostazioni</string>
|
||||
<string name="settings_category_title_ui">Interfaccia utente</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_system_theme">Sistema</string>
|
||||
<string name="settings_light_theme">Chiaro</string>
|
||||
@@ -209,7 +208,7 @@
|
||||
<string name="group_name_already_in_use">Il nome del gruppo è già in uso</string>
|
||||
<string name="group_updated">Gruppo aggiornato</string>
|
||||
<string name="selectColor">Seleziona il colore</string>
|
||||
<string name="setIcon">Imposta l\'icona</string>
|
||||
<string name="setIcon">Imposta l\'immagine</string>
|
||||
<string name="action_show_details">Mostra i dettagli</string>
|
||||
<string name="action_hide_details">Nascondi i dettagli</string>
|
||||
<string name="translate_platform">su Weblate</string>
|
||||
@@ -238,9 +237,9 @@
|
||||
<string name="settings_lock_on_opening_orientation">Blocca sull\'orientamento utilizzato all\'apertura della carta</string>
|
||||
<string name="failedLaunchingPhotoPicker">Impossibile trovare un\'app galleria supportata</string>
|
||||
<plurals name="groupCardCountWithArchived">
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> scheda (<xliff:g id="archivedCount">%2$d</xliff:g> archiviata)</item>
|
||||
<item quantity="many"><xliff:g>%1$d</xliff:g> schede (<xliff:g id="archivedCount">%2$d</xliff:g> archiviate)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> schede (<xliff:g id="archivedCount">%2$d</xliff:g> archiviate)</item>
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> carta (<xliff:g id="archivedCount">%2$d</xliff:g> archiviata)</item>
|
||||
<item quantity="many"><xliff:g>%1$d</xliff:g> carte (<xliff:g id="archivedCount">%2$d</xliff:g> archiviate)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> carte (<xliff:g id="archivedCount">%2$d</xliff:g> archiviate)</item>
|
||||
</plurals>
|
||||
<string name="previousCard">Precedente</string>
|
||||
<string name="nextCard">Successivo</string>
|
||||
@@ -275,4 +274,20 @@
|
||||
<string name="donate">Dona</string>
|
||||
<string name="openBackImageInGalleryApp">Apri l\'immagine posteriore nell\'app Galleria</string>
|
||||
<string name="setBarcodeHeight">Imposta l\'altezza del codice a barre</string>
|
||||
<string name="icon_header_click_text">Premi a lungo per modificare l\'immagine</string>
|
||||
<string name="show_note">Mostra nota</string>
|
||||
<string name="show_balance">Mostra bilancio</string>
|
||||
<string name="show_validity">Mostra validità</string>
|
||||
<string name="show_name_below_image_thumbnail">Mostra il nome sotto l\'immagine</string>
|
||||
<string name="permissionReadCardsLabel">Leggi le carte Catima</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Le applicazioni dovranno comunque richiedere l\'autorizzazione per ottenere l\'accesso</string>
|
||||
<string name="settings_allow_content_provider_read_title">Consenti ad altre applicazioni di accedere ai miei dati</string>
|
||||
<string name="permissionReadCardsDescription">Leggi le tue carte e tutti i suoi dettagli, comprese le note e le immagini</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Necessario per il funzionamento di alcuni scanner</string>
|
||||
<string name="settings_keep_screen_on_summary">Disattiva il timeout dello schermo durante la visualizzazione di una carta</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Disattiva il blocco dello schermo durante la visualizzazione di una carta</string>
|
||||
<string name="settings_oled_dark_summary">Riduce il consumo della batteria sui display OLED</string>
|
||||
<string name="settings_category_title_cards">Carte</string>
|
||||
<string name="settings_category_title_general">Generali</string>
|
||||
<string name="settings_category_title_privacy">Privacy</string>
|
||||
</resources>
|
||||
@@ -60,7 +60,6 @@
|
||||
<string name="settings_light_theme">ライト</string>
|
||||
<string name="settings_system_theme">システムに従う</string>
|
||||
<string name="settings_theme">テーマ</string>
|
||||
<string name="settings_category_title_ui">外観</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="starImage">お気に入りのスター</string>
|
||||
<string name="thumbnailDescription">サムネイル</string>
|
||||
@@ -205,4 +204,4 @@
|
||||
<string name="group_name_is_empty">グループ名を入力してください</string>
|
||||
<string name="shortcutSelectCard">カードを選択してください</string>
|
||||
<string name="translate_platform">on Weblate</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="addManually">직접 카드 ID 입력</string>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="addManually">직접 ID 입력</string>
|
||||
<string name="groupsList">그룹: <xliff:g>%s</xliff:g></string>
|
||||
<string name="all">모두</string>
|
||||
<string name="groups">그룹</string>
|
||||
@@ -11,9 +11,8 @@
|
||||
<string name="about_title_fmt"><xliff:g id="app_name">%s</xliff:g> 정보</string>
|
||||
<string name="settings_system_theme">시스템</string>
|
||||
<string name="settings_theme">테마</string>
|
||||
<string name="settings_category_title_ui">사용자 인터페이스</string>
|
||||
<string name="settings">설정</string>
|
||||
<string name="enterBarcodeInstructions">카드 ID를 입력하고 카드에서 사용하는 바코드 이미지를 선택하세요. 바코드를 사용하지 않는 경우 “이 카드는 바코드가 없음”을 선택하세요.</string>
|
||||
<string name="enterBarcodeInstructions">ID를 입력하고 아래에서 바코드 유형을 선택하거나 \"바코드가 없습니다\"를 선택하십시오.</string>
|
||||
<string name="selectBarcodeTitle">바코드 선택</string>
|
||||
<string name="about">정보</string>
|
||||
<string name="exporting">내보내는 중…</string>
|
||||
@@ -28,8 +27,8 @@
|
||||
<string name="sendLabel">보내기…</string>
|
||||
<string name="share">공유</string>
|
||||
<string name="copy_to_clipboard">ID를 클립보드에 복사</string>
|
||||
<string name="ok">확인</string>
|
||||
<string name="confirm">확인</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="confirm">확인하다</string>
|
||||
<string name="delete">삭제</string>
|
||||
<string name="edit">편집</string>
|
||||
<string name="save">저장</string>
|
||||
@@ -42,12 +41,10 @@
|
||||
<string name="cardId">카드 ID</string>
|
||||
<string name="note">노트</string>
|
||||
<string name="noMatchingGiftCards">아무것도 찾지 못했습니다. 검색어를 바꿔 보세요.</string>
|
||||
<string name="noGiftCards">먼저 \'+\' 버튼을 눌러 카드를 추가하세요.
|
||||
\n
|
||||
\nCatima는 카드를 장치에 보관하므로 어디서든 사용할 수 있습니다.</string>
|
||||
<string name="noGiftCards">+ 더하기 버튼을 클릭하여 카드를 추가하거나 ⋮ 메뉴에서 가져옵니다.</string>
|
||||
<string name="action_add">추가</string>
|
||||
<string name="action_search">검색</string>
|
||||
<string name="copy_to_clipboard_toast">카드 ID를 클립보드에 복사함</string>
|
||||
<string name="copy_to_clipboard_toast">클립보드에 ID 복사됨</string>
|
||||
<string name="importOptionApplicationTitle">다른 앱 사용</string>
|
||||
<string name="importOptionFilesystemExplanation">파일 시스템에서 파일을 선택합니다.</string>
|
||||
<string name="importOptionFilesystemTitle">파일 시스템에서 가져오기</string>
|
||||
@@ -60,14 +57,14 @@
|
||||
<string name="importSuccessfulTitle">가져오기 완료</string>
|
||||
<string name="noCardIdError">ID를 입력하지 않았습니다</string>
|
||||
<string name="storeName">이름</string>
|
||||
<string name="thumbnailDescription">카드 섬네일</string>
|
||||
<string name="thumbnailDescription">썸네일</string>
|
||||
<string name="importOptionApplicationButton">다른 앱 사용</string>
|
||||
<string name="failedParsingImportUriError">가져온 URI를 분석할 수 없음</string>
|
||||
<string name="noCardExistsError">해당 카드를 찾을 수 없습니다</string>
|
||||
<string name="moveUp">목록에서 위로 옮기기</string>
|
||||
<string name="leaveWithoutSaveTitle">저장하지 않고 종료</string>
|
||||
<string name="moveDown">목록에서 아래로 옮기기</string>
|
||||
<string name="noStoreError">매장을 입력하지 않음</string>
|
||||
<string name="moveUp">위로 이동</string>
|
||||
<string name="leaveWithoutSaveTitle">종료</string>
|
||||
<string name="moveDown">아래로 이동</string>
|
||||
<string name="noStoreError">이름이 입력되지 않았습니다</string>
|
||||
<string name="starImage">즐겨찾기 별</string>
|
||||
<string name="settings_display_barcode_max_brightness">바코드를 표시할 때 화면 밝기 높이기</string>
|
||||
<string name="deleteConfirmation">정말 이 카드를 삭제하시겠습니까\?</string>
|
||||
@@ -77,4 +74,195 @@
|
||||
</plurals>
|
||||
<string name="noGiftCardsGroup">카드를 몇 장 만든 다음 여기에서 그룹에 할당합니다.</string>
|
||||
<string name="noCardsMessage">먼저 카드 추가</string>
|
||||
</resources>
|
||||
<string name="leaveWithoutSaveConfirmation">저장하지 않고 나가시겠습니까\?</string>
|
||||
<string name="intent_import_card_from_url_share_text">너의 카드를 공유하고 싶습니다</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 카드</item>
|
||||
</plurals>
|
||||
<string name="group_name_already_in_use">이미 사용 중인 그룹 이름</string>
|
||||
<string name="addFromImage">갤러리에서 이미지 선택</string>
|
||||
<string name="expiryStateSentenceExpired">만료: <xliff:g>%s</xliff:g></string>
|
||||
<string name="card">카드</string>
|
||||
<string name="editBarcode">바코드 편집</string>
|
||||
<string name="expiryDate">만료일</string>
|
||||
<string name="never">절대</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">나는 당신과 몇 가지 카드를 공유하고 싶습니다</string>
|
||||
<string name="removeImage">이미지 삭제</string>
|
||||
<string name="takePhoto">사진을 찍다</string>
|
||||
<string name="yes">네</string>
|
||||
<string name="no">아니오</string>
|
||||
<string name="failedGeneratingShareURL">공유 가능한 URL을 생성할 수 없습니다. 이를 신고해 주십시오.</string>
|
||||
<string name="turn_flashlight_off">손전등 끄기</string>
|
||||
<string name="settings_locale">언어</string>
|
||||
<string name="settings_system_locale">시스템</string>
|
||||
<string name="selectColor">색상 선택</string>
|
||||
<string name="settings_theme_color">테마 색상</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_pink_theme">핑크색</string>
|
||||
<string name="settings_magenta_theme">자홍색</string>
|
||||
<string name="settings_violet_theme">보라색</string>
|
||||
<string name="settings_blue_theme">파란색</string>
|
||||
<string name="app_contributors">가능한 : <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="showMoreInfo">정보 보기</string>
|
||||
<string name="updateBalance">잔액 업데이트</string>
|
||||
<string name="failedToRetrieveImageFile">이미지 파일을 검색하지 못했습니다</string>
|
||||
<string name="barcodeLongPressMessage">갤러리 앱에서는 이미지만 열 수 있습니다</string>
|
||||
<string name="sort_by_name">이름</string>
|
||||
<string name="reverse">... 역순으로</string>
|
||||
<string name="sort_by">정렬 기준</string>
|
||||
<string name="version_history">버전 기록</string>
|
||||
<string name="credits">학점</string>
|
||||
<string name="license">특허</string>
|
||||
<string name="source_repository">소스 저장소</string>
|
||||
<string name="and_data_usage">및 데이터 사용량</string>
|
||||
<string name="on_google_play">Google Play에서</string>
|
||||
<string name="options">옵션</string>
|
||||
<string name="starred">별표</string>
|
||||
<string name="include_if_asking_support">지원을 요청하려면 다음 정보를 포함하십시오:</string>
|
||||
<string name="archive">보관소</string>
|
||||
<string name="unarchive">보관 취소</string>
|
||||
<string name="archived">보관된 카드</string>
|
||||
<string name="unarchived">보관되지 않은 카드</string>
|
||||
<string name="archiveList">보관소</string>
|
||||
<string name="failedLaunchingPhotoPicker">지원되는 갤러리 앱을 찾을 수 없습니다</string>
|
||||
<string name="welcome">Catima 오신 것을 환영합니다</string>
|
||||
<string name="validFromDate">유효</string>
|
||||
<string name="anyDate">모든 날짜</string>
|
||||
<string name="chooseValidFromDate">유효한 날짜를 선택하십시오</string>
|
||||
<string name="validFromSentence">유효한 위치: <xliff:g>%s</xliff:g></string>
|
||||
<string name="switchToFrontImage">전면 이미지로 전환</string>
|
||||
<string name="switchToBackImage">뒤로 이미지로 전환</string>
|
||||
<string name="switchToBarcode">바코드로 전환</string>
|
||||
<string name="donate">기부하다</string>
|
||||
<string name="show_name_below_image_thumbnail">이미지 축소판 아래에 이름 표시</string>
|
||||
<string name="sort">종류</string>
|
||||
<string name="sort_by_most_recently_used">가장 최근에 사용됨</string>
|
||||
<string name="settings_keep_screen_on">화면 켜기</string>
|
||||
<string name="unsupportedBarcodeType">이 바코드 유형은 아직 표시할 수 없습니다. 이후 버전의 앱에서 지원될 수 있습니다.</string>
|
||||
<string name="sort_by_expiry">만료</string>
|
||||
<string name="on_github">GitHub에서</string>
|
||||
<string name="report_error">오류 신고</string>
|
||||
<string name="rate_this_app">이 앱을 평가 해주세요</string>
|
||||
<string name="translate_platform">Weblate에서</string>
|
||||
<string name="shortcutSelectCard">카드 선택</string>
|
||||
<string name="group_edit">그룹 편집</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> 이 유효한 잔액이 아닌 것 같습니다.</string>
|
||||
<string name="chooseImportType">데이터 가져오기</string>
|
||||
<string name="barcodeId">바코드 값</string>
|
||||
<string name="setBackImage">이미지 설정</string>
|
||||
<string name="exportSuccessful">내보낸 데이터</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">화면 잠금 방지</string>
|
||||
<string name="group_updated">그룹 업데이트됨</string>
|
||||
<string name="moveBarcodeToTopOfScreen">바코드를 화면 상단으로 이동</string>
|
||||
<string name="height">키:</string>
|
||||
<string name="openFrontImageInGalleryApp">갤러리 앱에서 전면 이미지 열기</string>
|
||||
<string name="openBackImageInGalleryApp">갤러리 앱에서 다시 이미지 열기</string>
|
||||
<string name="setBarcodeHeight">바코드 높이 설정</string>
|
||||
<string name="setFrontImage">전면 이미지 설정</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 선택된</item>
|
||||
</plurals>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">저작권 © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">바코드를 스캔하려면 Catima가 카메라에 액세스해야 합니다. 권한 설정을 변경하려면 여기를 탭하세요.</string>
|
||||
<string name="exportOptionExplanation">데이터는 선택한 위치에 기록됩니다.</string>
|
||||
<string name="app_license">GPL v3+에 따라 라이선스가 부여된 무료 소프트웨어</string>
|
||||
<string name="failedOpeningFileManager">먼저 파일 관리자를 설치하십시오.</string>
|
||||
<string name="importSuccessful">가져온 데이터</string>
|
||||
<string name="group_name_is_empty">그룹 이름은 비워둘 수 없습니다</string>
|
||||
<string name="chooseExpiryDate">만료일 선택</string>
|
||||
<string name="settings_oled_dark">어두운 테마를 위한 검정색 배경</string>
|
||||
<string name="action_show_details">세부정보 표시</string>
|
||||
<string name="noGroups">+ 더하기 버튼을 클릭하여 분류할 그룹을 추가합니다.</string>
|
||||
<string name="deleteConfirmationGroup">그룹을 삭제하시겠습니까\?</string>
|
||||
<string name="errorReadingImage">이미지를 읽을 수 없습니다</string>
|
||||
<string name="expiryStateSentence">만료: <xliff:g>%s</xliff:g></string>
|
||||
<plurals name="balancePoints">
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> 포인트</item>
|
||||
</plurals>
|
||||
<string name="balanceSentence">잔액: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noBarcodeFound">바코드를 찾을 수 없습니다</string>
|
||||
<string name="points">포인트</string>
|
||||
<string name="balance">균형</string>
|
||||
<string name="currency">통화</string>
|
||||
<string name="accept">수용하다</string>
|
||||
<string name="importLoyaltyCardKeychain">로열티 카드 키체인에서 가져오기</string>
|
||||
<string name="wrongValueForBarcodeType">선택한 바코드 유형에 대해 값이 유효하지 않습니다</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">클립보드에 복사된 ID</string>
|
||||
<string name="updateBarcodeQuestionText">아이디를 변경하셨습니다. 동일한 값을 사용하도록 바코드도 업데이트하시겠습니까\?</string>
|
||||
<string name="exportPasswordHint">암호를 입력</string>
|
||||
<string name="updateBarcodeQuestionTitle">바코드 값을 업데이트하시겠습니까\?</string>
|
||||
<string name="turn_flashlight_on">손전등 켜기</string>
|
||||
<string name="exportPassword">내보내기를 보호하기 위한 암호 설정(선택 사항)</string>
|
||||
<string name="passwordRequired">비밀번호를 입력해주세요</string>
|
||||
<string name="action_hide_details">상세정보 표시</string>
|
||||
<string name="setIcon">썸네일 설정</string>
|
||||
<string name="show_balance">잔액 표시</string>
|
||||
<string name="help_translate_this_app">이 앱을 번역하도록 도와주세요</string>
|
||||
<string name="duplicateCard">복제하다</string>
|
||||
<string name="noUnarchivedCardsMessage">보관되지 않은 카드가 없습니다</string>
|
||||
<plurals name="groupCardCountWithArchived">
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> 카드 (<xliff:g id="archivedCount">%2$d</xliff:g> 보관됨)</item>
|
||||
</plurals>
|
||||
<string name="updateBalanceHint">금액을 입력하세요</string>
|
||||
<string name="show_note">메모 표시</string>
|
||||
<string name="show_validity">유효성 표시</string>
|
||||
<string name="icon_header_click_text">미리보기 이미지를 수정하려면 길게 누르세요</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="other">이 <xliff:g>%d</xliff:g> 카드를 영구적으로 삭제하시겠습니까\?</item>
|
||||
</plurals>
|
||||
<string name="card_ids_copied">복사된 ID</string>
|
||||
<string name="barcodeImageDescriptionWithType">이미지 <xliff:g>%s</xliff:g> 바코드</string>
|
||||
<string name="photos">사진</string>
|
||||
<string name="storageReadPermissionRequired">이 작업에 필요한 저장소를 읽을 수 있는 권한…</string>
|
||||
<string name="app_libraries">Libre 타사 라이브러리: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="cameraPermissionRequired">이 작업에 필요한 카메라 액세스 권한…</string>
|
||||
<string name="cameraPermissionDeniedTitle">카메라에 액세스할 수 없습니다</string>
|
||||
<string name="importOptionFilesystemButton">파일 시스템에서</string>
|
||||
<string name="app_copyright_old">Loyalty Card Keychain 기반
|
||||
\n저작권 © 2016–2020 Branden Archer</string>
|
||||
<string name="app_resources">Libre 타사 리소스: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="settings_lock_on_opening_orientation">카드를 열 때 사용되는 방향으로 고정</string>
|
||||
<string name="editGroup">그룹 편집: <xliff:g>%s</xliff:g></string>
|
||||
<string name="frontImageDescription">전면 이미지</string>
|
||||
<string name="backImageDescription">뒷면 이미지</string>
|
||||
<string name="currentBalanceSentence">현재 잔액: <xliff:g>%s</xliff:g></string>
|
||||
<string name="importCards">카드 가져오기</string>
|
||||
<string name="updateBalanceTitle">얼마를 썼습니까\?</string>
|
||||
<string name="newBalanceSentence">새 잔액: <xliff:g>%s</xliff:g></string>
|
||||
<string name="settings_card_orientation">바코드 방향</string>
|
||||
<string name="settings_follow_system_orientation">시스템을 따르세요</string>
|
||||
<string name="settings_portrait_orientation">초상화</string>
|
||||
<string name="settings_landscape_orientation">풍경</string>
|
||||
<string name="noGroupCards">이 그룹은 비어 있습니다</string>
|
||||
<string name="app_loyalty_card_keychain">로열티 카드 키체인</string>
|
||||
<string name="privacy_policy">개인 정보 정책</string>
|
||||
<string name="privacy_policy_popup_text">개인 정보 보호 정책 공지(일부 앱 스토어에서 필요):
|
||||
\n
|
||||
\n데이터가 전혀 수집되지 않습니다. 우리 앱은 무료 소프트웨어이기 때문에 누구나 확인할 수 있습니다.</string>
|
||||
<string name="importCatima">Catima에서 가져오기</string>
|
||||
<string name="importCatimaMessage">가져올 Catima에서 <i>catima.zip</i> 내보내기를 선택합니다.
|
||||
\n먼저 내보내기를 눌러 다른 Catima 앱의 가져오기/내보내기 메뉴에서 생성합니다.</string>
|
||||
<string name="importFidme">FidMe에서 가져오기</string>
|
||||
<string name="importFidmeMessage">가져올 FidMe에서 <i>fidme-export-request-xxxxxx.zip</i> 내보내기를 선택하고 나중에 바코드 유형을 수동으로 선택합니다.
|
||||
\n데이터 보호를 선택한 다음 먼저 내 데이터 추출을 눌러 FidMe 프로필에서 생성합니다.</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">가져올 포인트 카드 키체인에서 <i>LoyaltyCardKeychain.csv</i> 내보내기를 선택합니다.
|
||||
\n먼저 내보내기를 눌러 로열티 카드 키체인의 가져오기/내보내기 메뉴에서 만드십시오.</string>
|
||||
<string name="setBarcodeId">바코드 값 설정</string>
|
||||
<string name="importStocard">Stocard에서 가져오기</string>
|
||||
<string name="importStocardMessage">가져올 Stocard에서 <i>***.zip</i> 내보내기를 선택합니다.
|
||||
\nsupport@stocardapp.com으로 이메일을 보내 데이터 내보내기를 요청하면 받을 수 있습니다.</string>
|
||||
<string name="importVoucherVault">바우처 보관소에서 가져오기</string>
|
||||
<string name="importVoucherVaultMessage">가져올 Voucher Vault에서 <i>vouchervault.json</i> 내보내기를 선택합니다.
|
||||
\n먼저 바우처 금고에서 내보내기를 눌러 생성하세요.</string>
|
||||
<string name="sameAsCardId">아이디와 동일</string>
|
||||
<string name="settings_grey_theme">회색</string>
|
||||
<string name="settings_sky_blue_theme">하늘색</string>
|
||||
<string name="settings_green_theme">초록색</string>
|
||||
<string name="settings_brown_theme">갈색</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="other">보관소 보기 (<xliff:g>%1$d</xliff:g> 카)</item>
|
||||
</plurals>
|
||||
<string name="previousCard">이전의</string>
|
||||
<string name="nextCard">다음</string>
|
||||
<string name="failedToOpenUrl">먼저 웹 브라우저를 설치하십시오</string>
|
||||
</resources>
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
<string name="settings_light_theme">Šviesi</string>
|
||||
<string name="settings_system_theme">Sistemos</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Vartotojo sąsaja</string>
|
||||
<string name="settings">Nustatymai</string>
|
||||
<string name="starImage">Mėgstamiausia žvaigždė</string>
|
||||
<string name="thumbnailDescription">Miniatiūra</string>
|
||||
@@ -275,4 +274,4 @@
|
||||
<string name="switchToFrontImage">Perjungti į priekinį vaizdą</string>
|
||||
<string name="openFrontImageInGalleryApp">Atidarykite priekinį vaizdą galerijos programėlėje</string>
|
||||
<string name="donate">Aukoti</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
<string name="thumbnailDescription">Sīktēls</string>
|
||||
<string name="starImage">Izlases zvaigzne</string>
|
||||
<string name="settings">Iestatījumi</string>
|
||||
<string name="settings_category_title_ui">Interfeiss</string>
|
||||
<string name="settings_theme">Tēma</string>
|
||||
<string name="settings_system_theme">Sistēmas</string>
|
||||
<string name="settings_light_theme">Gaiša</string>
|
||||
|
||||
2
app/src/main/res/values-lzh/strings.xml
Normal file
2
app/src/main/res/values-lzh/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="action_add">Legg til</string>
|
||||
<string name="noGiftCards">Klikk på «+» (pluss)-knappen for å legge til eller importer kort fra «⋮»-menyen</string>
|
||||
<string name="noGiftCards">Klikk på «+» (pluss)-knappen for å legge til eller importere kort fra «⋮»-menyen.</string>
|
||||
<string name="storeName">Navn</string>
|
||||
<string name="note">Merknad</string>
|
||||
<string name="cardId">Kort-ID</string>
|
||||
@@ -49,7 +49,6 @@
|
||||
<string name="copy_to_clipboard_toast">ID kopiert til utklippstavle</string>
|
||||
<string name="thumbnailDescription">Miniatyrbilde</string>
|
||||
<string name="settings">Innstillinger</string>
|
||||
<string name="settings_category_title_ui">Brukergrensesnitt</string>
|
||||
<string name="settings_display_barcode_max_brightness">Lysere strekkodevisning</string>
|
||||
<string name="exportSuccessful">Data eksportert</string>
|
||||
<string name="importSuccessful">Data importert</string>
|
||||
@@ -63,7 +62,7 @@
|
||||
<string name="failedParsingImportUriError">Kunne ikke tolke importerings-URI</string>
|
||||
<string name="share">Del</string>
|
||||
<string name="barcodeNoBarcode">Det er ingen strekkode</string>
|
||||
<string name="noMatchingGiftCards">Resultatløst. Prøv å endre søket.</string>
|
||||
<string name="noMatchingGiftCards">Ingen resultater. Prøv å endre søket.</string>
|
||||
<string name="action_search">Søk</string>
|
||||
<string name="starImage">Favorittstjerne</string>
|
||||
<string name="unstar">Fjern fra favoritter</string>
|
||||
@@ -188,7 +187,7 @@
|
||||
<string name="license">Lisens</string>
|
||||
<string name="version_history">Versjonshistorikk</string>
|
||||
<string name="importCatimaMessage">Velg din <i>catima.zip</i>-eksport fra Catima å importere.
|
||||
\nOpprett den fra «Importer/Eksporter»-menyen i en annen Catima-app ved å trykke på «Eksporter» der først</string>
|
||||
\nOpprett den fra «Importer/Eksporter»-menyen i en annen Catima-app ved å trykke på «Eksporter» der først.</string>
|
||||
<string name="source_repository">Kildekode</string>
|
||||
<string name="on_github">på GitHub</string>
|
||||
<string name="and_data_usage">og bruk av data</string>
|
||||
@@ -207,7 +206,7 @@
|
||||
<string name="action_show_details">Vis detaljer</string>
|
||||
<string name="action_hide_details">Skjul detaljer</string>
|
||||
<string name="selectColor">Velg farge</string>
|
||||
<string name="setIcon">Sett ikon</string>
|
||||
<string name="setIcon">Sett miniatyrbilde</string>
|
||||
<string name="shortcutSelectCard">velg et kort</string>
|
||||
<string name="translate_platform">på Weblate</string>
|
||||
<string name="options">Alternativer</string>
|
||||
@@ -260,4 +259,28 @@
|
||||
<string name="storageReadPermissionRequired">Lagringslesetilgang kreves for denne handlingen …</string>
|
||||
<string name="validFromSentence">Gyldig fra: <xliff:g>%s</xliff:g></string>
|
||||
<string name="cameraPermissionRequired">Kameratilgang kreves for denne handlingen …</string>
|
||||
</resources>
|
||||
<string name="height">Høyde:</string>
|
||||
<string name="switchToFrontImage">Bytt til frontbilde</string>
|
||||
<string name="switchToBackImage">Bytt til baksidebildet</string>
|
||||
<string name="switchToBarcode">Bytt til strekkode</string>
|
||||
<string name="icon_header_click_text">Lang-trykk for å redigere miniatyrbilde</string>
|
||||
<string name="show_name_below_image_thumbnail">Vis navn under miniatyrbilde</string>
|
||||
<string name="show_balance">Vis saldo</string>
|
||||
<string name="show_note">Vis notat</string>
|
||||
<string name="show_validity">Vis gyldighet</string>
|
||||
<string name="openFrontImageInGalleryApp">Åpne frontbilde i galleriprogrammet</string>
|
||||
<string name="openBackImageInGalleryApp">Åpne baksidebilde i galleriprogram</string>
|
||||
<string name="setBarcodeHeight">Sett strekkodehøyde</string>
|
||||
<string name="donate">Doner</string>
|
||||
<string name="permissionReadCardsLabel">Les Catima-kort</string>
|
||||
<string name="permissionReadCardsDescription">Les kortene dine og detaljene om dem, inkludert notater og bilder</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Programmer vil fremdeles måtte forespørre tilgang for å få det innvilget</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Nødvendig for noen skannere</string>
|
||||
<string name="settings_keep_screen_on_summary">Skrur av skjermtidsavbrudd under visning av et kort</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Skrur av skjermlås under visning av et kort</string>
|
||||
<string name="settings_allow_content_provider_read_title">Tillat andre programmer tilgang til min data</string>
|
||||
<string name="settings_category_title_cards">Kort</string>
|
||||
<string name="settings_category_title_general">Generelt</string>
|
||||
<string name="settings_category_title_privacy">Personvern</string>
|
||||
<string name="settings_oled_dark_summary">Reduserer batteribruk for OLED-skjermer</string>
|
||||
</resources>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<string name="failedParsingImportUriError">Kan de import-uri niet verwerken</string>
|
||||
<string name="importExport">Importeren/Exporteren</string>
|
||||
<string name="exportName">Exporteren</string>
|
||||
<string name="importExportHelp">Door je gegevens te back-uppen, kun je ze overzetten naar een ander apparaat.</string>
|
||||
<string name="importExportHelp">Met een reservekopie van je gegevens kun je ze overzetten naar een ander apparaat.</string>
|
||||
<string name="importSuccessfulTitle">Importeren voltooid</string>
|
||||
<string name="importFailedTitle">Importeren mislukt</string>
|
||||
<string name="importFailed">Het importeren is mislukt</string>
|
||||
@@ -54,11 +54,10 @@
|
||||
<string name="copy_to_clipboard_toast">De kaart-id is gekopieerd naar het klembord</string>
|
||||
<string name="thumbnailDescription">Miniatuurvoorbeeld</string>
|
||||
<string name="settings">Instellingen</string>
|
||||
<string name="settings_category_title_ui">Vormgeving</string>
|
||||
<string name="settings_theme">Thema</string>
|
||||
<string name="settings_system_theme">Systeem</string>
|
||||
<string name="settings_light_theme">Licht</string>
|
||||
<string name="settings_dark_theme">Donker</string>
|
||||
<string name="settings_system_theme">Systeemthema</string>
|
||||
<string name="settings_light_theme">Licht thema</string>
|
||||
<string name="settings_dark_theme">Donker thema</string>
|
||||
<string name="settings_display_barcode_max_brightness">Scherm helderder maken bij tonen van barcode</string>
|
||||
<string name="intent_import_card_from_url_share_text">Ik wil een klantenkaart met je delen</string>
|
||||
<string name="all">Alles</string>
|
||||
@@ -68,7 +67,7 @@
|
||||
<string name="exportSuccessful">De gegevens zijn geëxporteerd</string>
|
||||
<string name="groups">Groepen</string>
|
||||
<string name="enter_group_name">Voer een groepsnaam in</string>
|
||||
<string name="starImage">Favoriete ster</string>
|
||||
<string name="starImage">Favorietster</string>
|
||||
<string name="app_copyright_old">Gebaseerd op Klantenkaartkluis,
|
||||
\ncopyright ©2016–2020 Branden Archer</string>
|
||||
<string name="unstar">Verwijderen uit favorieten</string>
|
||||
@@ -204,7 +203,7 @@
|
||||
<string name="editGroup">Groep bewerken: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noGiftCardsGroup">Voeg kaarten toe om ze hier te kunnen groeperen.</string>
|
||||
<string name="group_edit">Groep bewerken</string>
|
||||
<string name="setIcon">Kies een pictogram</string>
|
||||
<string name="setIcon">Miniatuur instellen</string>
|
||||
<string name="selectColor">Kies een kleur</string>
|
||||
<string name="action_show_details">Details tonen</string>
|
||||
<string name="action_hide_details">Details verbergen</string>
|
||||
@@ -268,4 +267,20 @@
|
||||
<string name="openFrontImageInGalleryApp">Voorzijde openen in galerij-app</string>
|
||||
<string name="openBackImageInGalleryApp">Achterzijde openen in galerij-app</string>
|
||||
<string name="donate">Doneren</string>
|
||||
<string name="icon_header_click_text">Houd lang ingedrukt om miniatuur te bewerken</string>
|
||||
<string name="show_balance">Saldo tonen</string>
|
||||
<string name="show_name_below_image_thumbnail">Naam onder miniatuur tonen</string>
|
||||
<string name="show_note">Aantekening tonen</string>
|
||||
<string name="show_validity">Geldigheid tonen</string>
|
||||
<string name="permissionReadCardsLabel">Catimakaarten uitlezen</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Let op: apps moeten nog steeds om toestemming vragen</string>
|
||||
<string name="permissionReadCardsDescription">Lees alle kaarten uit, inclusief alle details, aantekeningen en afbeeldingen</string>
|
||||
<string name="settings_allow_content_provider_read_title">Andere apps toegang geven tot gegevens</string>
|
||||
<string name="settings_keep_screen_on_summary">Laat het scherm niet op zwart gaan tijdens het bekijken van een kaart</string>
|
||||
<string name="settings_category_title_privacy">Privacy</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Benodigd voor de werking van sommige scanners</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Schakelt schermvergrendeling uit tijdens het bekijken van een kaart</string>
|
||||
<string name="settings_oled_dark_summary">Verlaagt het accuverbruik op oledschermen</string>
|
||||
<string name="settings_category_title_cards">Kaarten</string>
|
||||
<string name="settings_category_title_general">Algemeen</string>
|
||||
</resources>
|
||||
@@ -49,7 +49,6 @@
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> seleccionadas</item>
|
||||
</plurals>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="settings_category_title_ui">Interfàcia utilizaire</string>
|
||||
<string name="settings_theme">Tèma</string>
|
||||
<string name="noGiftCards">Clicatz lo boton + per apondre una carta o n’importar una menú ⋮</string>
|
||||
<string name="noMatchingGiftCards">Cap de resultat. Ensajatz de modificar vòstra recèrca.</string>
|
||||
@@ -66,4 +65,4 @@
|
||||
<string name="settings_theme_color">Color del tèma</string>
|
||||
<string name="settings_locale">Lenga</string>
|
||||
<string name="noGiftCardsGroup">Creatz de cartas puèi ligatz-las al grop aicí.</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
<string name="copy_to_clipboard_toast">Skopiowano ID do schowka</string>
|
||||
<string name="thumbnailDescription">Miniaturka</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="settings_category_title_ui">Interfejs użytkownika</string>
|
||||
<string name="settings_theme">Motyw</string>
|
||||
<string name="settings_system_theme">Systemowy</string>
|
||||
<string name="settings_light_theme">Jasny</string>
|
||||
@@ -198,7 +197,7 @@
|
||||
<string name="help_translate_this_app">Pomóż przetłumaczyć tę aplikację</string>
|
||||
<string name="source_repository">Repozytorium Źródłowe</string>
|
||||
<string name="report_error">Zgłoś Błąd</string>
|
||||
<string name="setIcon">Ustaw ikonę</string>
|
||||
<string name="setIcon">Ustaw miniaturę</string>
|
||||
<string name="on_github">na GitHub\'ie</string>
|
||||
<string name="selectColor">Wybierz kolor</string>
|
||||
<string name="version_history">Historia wersji</string>
|
||||
@@ -281,4 +280,21 @@
|
||||
<string name="openFrontImageInGalleryApp">Otwórz obraz z przodu w aplikacji galeria</string>
|
||||
<string name="setBarcodeHeight">Ustaw wysokość kodu kreskowego</string>
|
||||
<string name="donate">Darowizna</string>
|
||||
</resources>
|
||||
<string name="openBackImageInGalleryApp">Otwórz obraz z powrotem w aplikacji galerii</string>
|
||||
<string name="icon_header_click_text">Przytrzymaj, aby edytować miniaturę</string>
|
||||
<string name="show_name_below_image_thumbnail">Pokaż nazwę pod miniaturką zdjęcia</string>
|
||||
<string name="show_balance">Pokaż balans</string>
|
||||
<string name="show_validity">Pokaż ważność</string>
|
||||
<string name="show_note">Pokaż notatkę</string>
|
||||
<string name="permissionReadCardsLabel">Odczytaj Karty Catima</string>
|
||||
<string name="permissionReadCardsDescription">Odczytaj swoje karty i ich szczegóły, włącznie z notatkami i obrazkami</string>
|
||||
<string name="settings_allow_content_provider_read_title">Pozwól innym aplikacjom na dostęp do moich danych</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Potrzebne aby niektóre skanery działały</string>
|
||||
<string name="settings_keep_screen_on_summary">Wyłącza wygaszanie ekranu kiedy wyświetlana jest karta</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Wyłącza blokadę ekranu kiedy wyświetlana jest karta</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Aplikacje będą wymagały pozwolenia aby otrzymać dostęp do danych</string>
|
||||
<string name="settings_oled_dark_summary">Zmniejsza zużycie baterii na wyświetlaczach OLED</string>
|
||||
<string name="settings_category_title_cards">Karty</string>
|
||||
<string name="settings_category_title_general">Ogólne</string>
|
||||
<string name="settings_category_title_privacy">Prywatność</string>
|
||||
</resources>
|
||||
@@ -8,12 +8,12 @@
|
||||
<string name="storeName">Nome</string>
|
||||
<string name="note">Nota</string>
|
||||
<string name="barcodeType">Tipo de código de barras</string>
|
||||
<string name="barcodeNoBarcode">Sem código de barras</string>
|
||||
<string name="barcodeNoBarcode">Não há código de barras</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="noGiftCards">Clique no botão + para adicionar um cartão ou importe-o no menu ⋮.</string>
|
||||
<string name="noBarcode">Sem código de barras</string>
|
||||
<string name="noBarcode">sem código de barras</string>
|
||||
<string name="unstar">Retirar dos favoritos</string>
|
||||
<string name="importOptionFilesystemButton">Do sistema de ficheiros</string>
|
||||
<string name="importOptionApplicationTitle">Usar outra aplicação</string>
|
||||
@@ -57,7 +57,6 @@
|
||||
<string name="all">Todos</string>
|
||||
<string name="deleteConfirmationGroup">Eliminar o grupo\?</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="settings_category_title_ui">Interface do utilizador</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_system_theme">Sistema</string>
|
||||
<string name="settings_light_theme">Claro</string>
|
||||
@@ -209,7 +208,7 @@
|
||||
<string name="editGroup">A editar grupo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noGiftCardsGroup">Crie alguns cartões e atribua-os depois ao grupo aqui.</string>
|
||||
<string name="selectColor">Selecionar cor</string>
|
||||
<string name="setIcon">Definir ícone</string>
|
||||
<string name="setIcon">Definir miniatura</string>
|
||||
<string name="action_show_details">Mostrar detalhes</string>
|
||||
<string name="action_hide_details">Ocultar detalhes</string>
|
||||
<string name="shortcutSelectCard">Selecione um cartão</string>
|
||||
@@ -271,8 +270,24 @@
|
||||
<string name="height">Altura:</string>
|
||||
<string name="switchToBackImage">Mudar para a imagem de trás</string>
|
||||
<string name="switchToBarcode">Mudar para o código de barras</string>
|
||||
<string name="openFrontImageInGalleryApp">Abra a imagem frontal no aplicativo da galeria</string>
|
||||
<string name="openBackImageInGalleryApp">Abra a imagem traseira no aplicativo da galeria</string>
|
||||
<string name="openFrontImageInGalleryApp">Abrir a imagem frontal na aplicação da galeria</string>
|
||||
<string name="openBackImageInGalleryApp">Abrir a imagem traseira na aplicação da galeria</string>
|
||||
<string name="setBarcodeHeight">Definir altura do código de barras</string>
|
||||
<string name="donate">Doar</string>
|
||||
</resources>
|
||||
<string name="show_validity">Mostrar validade</string>
|
||||
<string name="show_balance">Mostrar saldo</string>
|
||||
<string name="permissionReadCardsLabel">Ler Cartas Catima</string>
|
||||
<string name="permissionReadCardsDescription">Leia seus cartões e todos os seus detalhes, incluindo notas e imagens</string>
|
||||
<string name="show_note">Mostrar nota</string>
|
||||
<string name="show_name_below_image_thumbnail">Mostrar nome abaixo da miniatura do ícone</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Desativa o bloqueio de tela ao visualizar um cartão</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Necessário para alguns scanners funcionarem</string>
|
||||
<string name="settings_keep_screen_on_summary">Desativa o tempo limite da tela ao visualizar um cartão</string>
|
||||
<string name="settings_allow_content_provider_read_title">Permitir que outros aplicativos acessem meus dados</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Os aplicativos ainda terão que solicitar permissão para receber acesso</string>
|
||||
<string name="settings_oled_dark_summary">Reduz o uso da bateria em telas OLED</string>
|
||||
<string name="icon_header_click_text">Pressione e segure para editar o icone</string>
|
||||
<string name="settings_category_title_cards">cartões</string>
|
||||
<string name="settings_category_title_general">geral</string>
|
||||
<string name="settings_category_title_privacy">Privacidade</string>
|
||||
</resources>
|
||||
@@ -43,7 +43,6 @@
|
||||
<string name="importOptionApplicationTitle">Utilizați o altă aplicație</string>
|
||||
<string name="starImage">Steaua preferată</string>
|
||||
<string name="settings">Setări</string>
|
||||
<string name="settings_category_title_ui">Interfață utilizator</string>
|
||||
<string name="intent_import_card_from_url_share_text">Vreau să împărtășesc o carte cu tine</string>
|
||||
<string name="moveUp">Mutarea în sus</string>
|
||||
<string name="editCardTitle">Editare card</string>
|
||||
@@ -103,4 +102,4 @@
|
||||
<string name="importCatima">Importați din Catima</string>
|
||||
<string name="importStocard">Importați din Stocard</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Aș dori să partajez niște carduri cu tine</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
<string name="copy_to_clipboard_toast">Номер скопирован в буфер обмена</string>
|
||||
<string name="thumbnailDescription">Логотип</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="settings_category_title_ui">Внешний вид</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_system_theme">Системная</string>
|
||||
<string name="settings_light_theme">Светлая</string>
|
||||
@@ -93,7 +92,7 @@
|
||||
<string name="moveBarcodeToTopOfScreen">Переместить штрих-код в верхнюю часть экрана</string>
|
||||
<string name="currency">Валюта</string>
|
||||
<string name="chooseExpiryDate">Выбор срока действия</string>
|
||||
<string name="never">Никогда</string>
|
||||
<string name="never">Нет</string>
|
||||
<string name="expiryDate">Срок действия</string>
|
||||
<string name="editBarcode">Изменить штрих-код</string>
|
||||
<string name="card">Карта</string>
|
||||
@@ -212,10 +211,10 @@
|
||||
<string name="group_edit">Изменить группу</string>
|
||||
<string name="group_name_already_in_use">Такое название группы уже существует</string>
|
||||
<string name="noGiftCardsGroup">Создайте несколько карт, а затем распределите их по группам здесь.</string>
|
||||
<string name="setIcon">Выбор значка</string>
|
||||
<string name="setIcon">Выбор миниатюры</string>
|
||||
<string name="selectColor">Выбрать цвет</string>
|
||||
<string name="action_hide_details">Скрыть детали</string>
|
||||
<string name="action_show_details">Показать детали</string>
|
||||
<string name="action_hide_details">Скрытие подробностей</string>
|
||||
<string name="action_show_details">Отображение подробностей</string>
|
||||
<string name="translate_platform">на Weblate</string>
|
||||
<string name="shortcutSelectCard">Выбор карты</string>
|
||||
<string name="options">Параметры</string>
|
||||
@@ -282,4 +281,20 @@
|
||||
<string name="openFrontImageInGalleryApp">Открыть лицевое изображение в приложении галереи</string>
|
||||
<string name="openBackImageInGalleryApp">Открыть заднее изображение в приложении галереи</string>
|
||||
<string name="donate">Пожертвовать</string>
|
||||
<string name="icon_header_click_text">Долгое нажатие для редактирования миниатюры</string>
|
||||
<string name="show_note">Показывать примечание</string>
|
||||
<string name="show_validity">Показывать срок действия</string>
|
||||
<string name="show_name_below_image_thumbnail">Показывать название под миниатюрой</string>
|
||||
<string name="show_balance">Показывать баланс</string>
|
||||
<string name="permissionReadCardsDescription">Чтение ваших карт и всех их данных, включая заметки и изображения</string>
|
||||
<string name="permissionReadCardsLabel">Чтение карт Catima</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Приложениям по-прежнему придётся запрашивать разрешение для получения доступа</string>
|
||||
<string name="settings_allow_content_provider_read_title">Разрешать другим приложениям доступ к моим данным</string>
|
||||
<string name="settings_category_title_privacy">Конфиденциальность</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Необходимо для работы некоторых сканеров</string>
|
||||
<string name="settings_keep_screen_on_summary">Отключение функции автоотключения экрана при отображении карты</string>
|
||||
<string name="settings_category_title_cards">Карты</string>
|
||||
<string name="settings_category_title_general">Общие</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Отключение функции блокировки экрана при отображении карты</string>
|
||||
<string name="settings_oled_dark_summary">Снижение расхода заряда батареи на OLED-экранах</string>
|
||||
</resources>
|
||||
@@ -46,7 +46,6 @@
|
||||
<string name="copy_to_clipboard_toast">ID skopírované do schránky</string>
|
||||
<string name="thumbnailDescription">Miniatúra</string>
|
||||
<string name="settings">Nastavenia</string>
|
||||
<string name="settings_category_title_ui">Používateľské prostredie</string>
|
||||
<string name="settings_display_barcode_max_brightness">Zvýšiť jas pri zobrazení čiarového kódu </string>
|
||||
<string name="deleteTitle">Odstrániť kartu</string>
|
||||
<string name="deleteConfirmation">Naozaj chcete túto kartu odstrániť?</string>
|
||||
@@ -151,7 +150,7 @@
|
||||
<string name="yes">Áno</string>
|
||||
<string name="importStocard">Import z aplikácie Stocard</string>
|
||||
<string name="selectColor">Vybrať farbu</string>
|
||||
<string name="setIcon">Nastaviť ikonu</string>
|
||||
<string name="setIcon">Nastavenie miniatúry</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_pink_theme">Ružová</string>
|
||||
<string name="settings_magenta_theme">Purpurová</string>
|
||||
@@ -267,4 +266,28 @@
|
||||
<string name="noUnarchivedCardsMessage">Nie sú žiadne karty vrátené z archívu</string>
|
||||
<string name="newBalanceSentence">Nový zostatok: <xliff:g>%s</xliff:g></string>
|
||||
<string name="failedLaunchingPhotoPicker">Nepodarilo sa nájsť podporovanú aplikáciu galérie</string>
|
||||
<string name="show_note">Zobraziť poznámku</string>
|
||||
<string name="icon_header_click_text">Dlhým stlačením upravíte miniatúru</string>
|
||||
<string name="settings_category_title_general">Všeobecné</string>
|
||||
<string name="settings_category_title_privacy">Súkromie</string>
|
||||
<string name="settings_keep_screen_on_summary">Zakázanie časového limitu obrazovky počas prezerania karty</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Potrebné pre fungovanie niektorých skenerov</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Aplikácie budú musieť stále žiadať o povolenie, aby im bol udelený prístup</string>
|
||||
<string name="openBackImageInGalleryApp">Otvorenie spätného obrázka v aplikácii galéria</string>
|
||||
<string name="openFrontImageInGalleryApp">Otvorenie predného obrázka v aplikácii galéria</string>
|
||||
<string name="setBarcodeHeight">Nastavenie výšky čiarového kódu</string>
|
||||
<string name="show_balance">Ukážte rovnováhu</string>
|
||||
<string name="show_name_below_image_thumbnail">Zobraziť názov pod miniatúrou obrázka</string>
|
||||
<string name="show_validity">Zobraziť platnosť</string>
|
||||
<string name="permissionReadCardsLabel">Načítať Catima karty</string>
|
||||
<string name="permissionReadCardsDescription">Čítanie kariet a všetkých ich detailov vrátane poznámok a obrázkov</string>
|
||||
<string name="switchToBackImage">Prepnutie na zadný obrázok</string>
|
||||
<string name="height">Výška:</string>
|
||||
<string name="switchToFrontImage">Prepnutie na predný obrázok</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Zakázanie uzamknutia obrazovky počas prezerania karty</string>
|
||||
<string name="settings_allow_content_provider_read_title">Povolenie prístupu k mojim údajom iným aplikáciám</string>
|
||||
<string name="settings_oled_dark_summary">Znižuje spotrebu batérie na displejoch OLED</string>
|
||||
<string name="switchToBarcode">Prepnutie na čiarový kód</string>
|
||||
<string name="settings_category_title_cards">Karty</string>
|
||||
<string name="donate">Darujte</string>
|
||||
</resources>
|
||||
@@ -46,7 +46,6 @@
|
||||
<string name="copy_to_clipboard_toast">ID številka je kopirana v odložišče</string>
|
||||
<string name="thumbnailDescription">Sličica</string>
|
||||
<string name="settings">Nastavitve</string>
|
||||
<string name="settings_category_title_ui">Uporabniški vmesnik</string>
|
||||
<string name="settings_display_barcode_max_brightness">Povečaj osvetljenost prikaza črtne kode</string>
|
||||
<string name="deleteTitle">Odstrani kartico zvestobe</string>
|
||||
<string name="deleteConfirmation">Prosim potrdite, če želite izbrisati to kartico\?</string>
|
||||
@@ -274,4 +273,4 @@
|
||||
<string name="validFromDate">Velja od</string>
|
||||
<string name="anyDate">Katerikoli datum</string>
|
||||
<string name="chooseValidFromDate">Izberite datum začetka veljavnosti</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
<string name="settings_dark_theme">Mörkt</string>
|
||||
<string name="settings_light_theme">Ljust</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Användargränssnitt</string>
|
||||
<string name="settings">Inställningar</string>
|
||||
<string name="thumbnailDescription">Miniatyrbild</string>
|
||||
<string name="copy_to_clipboard_toast">ID:t kopierat till Urklipp</string>
|
||||
@@ -260,4 +259,4 @@
|
||||
<string name="updateBalanceTitle">Hur mycket spenderade du\?</string>
|
||||
<string name="updateBalanceHint">Ange summa</string>
|
||||
<string name="newBalanceSentence">Ny balans: <xliff:g>%s</xliff:g></string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -100,7 +100,6 @@
|
||||
<string name="settings_light_theme">Açık</string>
|
||||
<string name="settings_system_theme">Sistem</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_category_title_ui">Kullanıcı arayüzü</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="starImage">Sık kullanılan yıldız</string>
|
||||
<string name="thumbnailDescription">Küçük resim</string>
|
||||
@@ -205,7 +204,7 @@
|
||||
<string name="group_name_is_empty">Grup adı boş olamaz</string>
|
||||
<string name="group_updated">Grup güncellendi</string>
|
||||
<string name="selectColor">Renk seç</string>
|
||||
<string name="setIcon">Simge ayarla</string>
|
||||
<string name="setIcon">Küçük resim ayarla</string>
|
||||
<string name="action_show_details">Ayrıntıları göster</string>
|
||||
<string name="action_hide_details">Ayrıntıları gizle</string>
|
||||
<string name="translate_platform">Weblate\'de</string>
|
||||
@@ -268,4 +267,20 @@
|
||||
<string name="setBarcodeHeight">Barkod yüksekliğini ayarla</string>
|
||||
<string name="openFrontImageInGalleryApp">Ön resmi galeri uygulamasında aç</string>
|
||||
<string name="openBackImageInGalleryApp">Arka resmi galeri uygulamasında aç</string>
|
||||
<string name="icon_header_click_text">Küçük resmi düzenlemek için uzun basın</string>
|
||||
<string name="show_name_below_image_thumbnail">Küçük resmin altında adı göster</string>
|
||||
<string name="show_note">Notu göster</string>
|
||||
<string name="show_balance">Bakiyeyi göster</string>
|
||||
<string name="show_validity">Geçerliliği göster</string>
|
||||
<string name="permissionReadCardsLabel">Catima kartlarını oku</string>
|
||||
<string name="permissionReadCardsDescription">Kartlarınızı ve tüm detaylarını, notlar ve resimler de dahil olmak üzere okuyun</string>
|
||||
<string name="settings_keep_screen_on_summary">Ekran zaman aşımını bir kart görüntülerken devre dışı bırakır</string>
|
||||
<string name="settings_allow_content_provider_read_title">Diğer uygulamaların verilerime erişmesine izin ver</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Bazı tarayıcıların çalışması için gereklidir</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Ekran kilidini bir kart görüntülerken devre dışı bırakır</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Uygulamaların erişim izni almak için yine de izin istemesi gerekecek</string>
|
||||
<string name="settings_category_title_general">Genel</string>
|
||||
<string name="settings_category_title_privacy">Gizlilik</string>
|
||||
<string name="settings_oled_dark_summary">OLED ekranlarda güç kullanımını düşürür</string>
|
||||
<string name="settings_category_title_cards">Kartlar</string>
|
||||
</resources>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="importCatima">Імпорт з Catima</string>
|
||||
<string name="accept">Прийняти</string>
|
||||
<string name="privacy_policy">Політика конфіденційності</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="app_loyalty_card_keychain">Брелок для картки лояльності</string>
|
||||
<string name="chooseImportType">Імпортувати дані з</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> здається, не є дійсним залишком.</string>
|
||||
<string name="points">Бали</string>
|
||||
@@ -79,7 +79,6 @@
|
||||
<string name="settings_light_theme">Світла</string>
|
||||
<string name="settings_system_theme">Системна</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_category_title_ui">Інтерфейс користувача</string>
|
||||
<string name="settings">Налаштування</string>
|
||||
<string name="starImage">Улюблена зірка</string>
|
||||
<string name="thumbnailDescription">Ескіз</string>
|
||||
@@ -213,7 +212,7 @@
|
||||
<string name="group_name_already_in_use">Група з такою назвою вже існує</string>
|
||||
<string name="noGiftCardsGroup">Створите кілька карток, та призначте їх до групи тут.</string>
|
||||
<string name="selectColor">Вибір кольору</string>
|
||||
<string name="setIcon">Вибір іконки</string>
|
||||
<string name="setIcon">Вибір мініатюри</string>
|
||||
<string name="action_show_details">Показати деталі</string>
|
||||
<string name="action_hide_details">Сховати деталі</string>
|
||||
<string name="translate_platform">на Weblate</string>
|
||||
@@ -274,4 +273,28 @@
|
||||
<string name="anyDate">Будь-яка дата</string>
|
||||
<string name="chooseValidFromDate">Виберіть термін дії з дати</string>
|
||||
<string name="validFromSentence">Дійсний від: <xliff:g>%s</xliff:g></string>
|
||||
<string name="switchToFrontImage">Перемкнутися на лицьову сторону</string>
|
||||
<string name="switchToBackImage">Перемкнутися на зворотню сторону</string>
|
||||
<string name="switchToBarcode">Перемкнутися на штрих-код</string>
|
||||
<string name="openFrontImageInGalleryApp">Відкрити зображення лицьової сторони у додату галереї</string>
|
||||
<string name="openBackImageInGalleryApp">Відкрити зображення тильної сторони у додатку галереї</string>
|
||||
<string name="setBarcodeHeight">Встановити висоту штрих-коду</string>
|
||||
<string name="height">Висота:</string>
|
||||
<string name="donate">Пожертвувати</string>
|
||||
<string name="icon_header_click_text">Тривале натискання для редагування мініатюри</string>
|
||||
<string name="show_name_below_image_thumbnail">Показати назву під мініатюрою зображення</string>
|
||||
<string name="show_note">Показати примітку</string>
|
||||
<string name="show_validity">Показати термін дії</string>
|
||||
<string name="show_balance">Показати баланс</string>
|
||||
<string name="permissionReadCardsLabel">Читати карти Catima</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Додатки все ще муситимуть запитувати дозвіл на отримання доступу</string>
|
||||
<string name="permissionReadCardsDescription">Читати всі ваші карти та їх деталі, в тому числі нотатки та зображення</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Необхідно для роботи деяких сканерів</string>
|
||||
<string name="settings_keep_screen_on_summary">Вимикає тайм-аут екрана під час перегляду картки</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Вимикає блокування екрана під час перегляду картки</string>
|
||||
<string name="settings_allow_content_provider_read_title">Дозволити іншим програмам доступ до моїх даних</string>
|
||||
<string name="settings_oled_dark_summary">Зменшує використання батареї на екранах з OLED</string>
|
||||
<string name="settings_category_title_cards">Картки</string>
|
||||
<string name="settings_category_title_general">Загальні</string>
|
||||
<string name="settings_category_title_privacy">Конфіденційність</string>
|
||||
</resources>
|
||||
@@ -4,16 +4,16 @@
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 卡片</item>
|
||||
</plurals>
|
||||
<string name="app_libraries">第三方自由程序库: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">版本: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_libraries">第三方自由程序库:<xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">版本:<xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">关于 <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="card">卡片</string>
|
||||
<string name="addManually">手动输入卡号</string>
|
||||
<string name="settings_system_theme">系统设置</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">版权所有 © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="importOptionApplicationButton">使用其他应用</string>
|
||||
<string name="importOptionFilesystemButton">从文件系统</string>
|
||||
<string name="unstar">从收藏中删除</string>
|
||||
<string name="importOptionFilesystemButton">使用文件系统</string>
|
||||
<string name="unstar">从收藏中移除</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">我想和你分享一些卡片</string>
|
||||
<string name="copy_to_clipboard_multiple_toast">卡号已复制到剪贴板</string>
|
||||
<string name="wrongValueForBarcodeType">该值对所选条形码类型无效</string>
|
||||
@@ -74,36 +74,35 @@
|
||||
<string name="settings_dark_theme">暗色</string>
|
||||
<string name="settings_light_theme">浅色</string>
|
||||
<string name="settings_theme">主题</string>
|
||||
<string name="settings_category_title_ui">用户界面</string>
|
||||
<string name="settings">设置</string>
|
||||
<string name="thumbnailDescription">缩略图</string>
|
||||
<string name="copy_to_clipboard_toast">已复制卡号到剪贴板</string>
|
||||
<string name="enterBarcodeInstructions">输入卡号,并从下面选择其条码类型,或选择\"无条码\"。</string>
|
||||
<string name="enterBarcodeInstructions">输入卡号,并从下面选择其条码类型,或选择“无条码”。</string>
|
||||
<string name="selectBarcodeTitle">选择条码</string>
|
||||
<string name="app_resources">第三方自由资源:<xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="app_license">本软件为自由软件,使用 GPLv3+ 许可证</string>
|
||||
<string name="app_copyright_old">基于 Loyalty Card Keychain
|
||||
\n版权所有 © 2016–2020 Branden Archer</string>
|
||||
\nCopyright © 2016–2020 Branden Archer</string>
|
||||
<string name="about">关于</string>
|
||||
<string name="importOptionApplicationExplanation">使用任何应用程序或您喜欢的文件管理器打开文件。</string>
|
||||
<string name="importOptionApplicationTitle">使用其他应用</string>
|
||||
<string name="importOptionFilesystemExplanation">请从文件系统选择文件.</string>
|
||||
<string name="importOptionFilesystemExplanation">请从文件系统选择文件。</string>
|
||||
<string name="importOptionFilesystemTitle">从文件系统导入</string>
|
||||
<string name="exportOptionExplanation">导出的数据将储存至你选择的位置.</string>
|
||||
<string name="exportOptionExplanation">导出的数据将储存至你选择的位置。</string>
|
||||
<string name="cameraPermissionDeniedTitle">无法访问相机</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Catima需要访问您的相机来扫描条形码. 轻触这里以更改您的权限设置。</string>
|
||||
<string name="exporting">导出中…</string>
|
||||
<string name="importing">导入中…</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Catima 需要访问您的相机来扫描条形码,轻触这里以更改您的权限设置。</string>
|
||||
<string name="exporting">导出中……</string>
|
||||
<string name="importing">导入中……</string>
|
||||
<string name="exportFailed">无法导出卡片</string>
|
||||
<string name="exportFailedTitle">导出失败</string>
|
||||
<string name="exportSuccessfulTitle">导出成功</string>
|
||||
<string name="importFailed">无法导入卡片</string>
|
||||
<string name="importFailedTitle">导入失败</string>
|
||||
<string name="importSuccessfulTitle">导入成功</string>
|
||||
<string name="importExportHelp">备份数据允许你将其移动到另一个设备上。</string>
|
||||
<string name="importExportHelp">备份你的数据以允许将其移动到另一个设备上。</string>
|
||||
<string name="exportName">导出</string>
|
||||
<string name="importExport">导入/导出</string>
|
||||
<string name="failedParsingImportUriError">无法解析导入的URI</string>
|
||||
<string name="failedParsingImportUriError">无法解析导入 URI</string>
|
||||
<string name="noCardExistsError">找不到卡片</string>
|
||||
<string name="noCardIdError">未输入卡号</string>
|
||||
<string name="noStoreError">未输入卡片名称</string>
|
||||
@@ -113,7 +112,7 @@
|
||||
<string name="editCardTitle">编辑卡片</string>
|
||||
<string name="addCardTitle">添加卡片</string>
|
||||
<string name="scanCardBarcode">扫描条码</string>
|
||||
<string name="sendLabel">发送…</string>
|
||||
<string name="sendLabel">发送……</string>
|
||||
<string name="share">分享</string>
|
||||
<string name="copy_to_clipboard">复制卡号到剪贴板</string>
|
||||
<string name="ok">确定</string>
|
||||
@@ -124,22 +123,22 @@
|
||||
<string name="cancel">取消</string>
|
||||
<string name="star">添加到收藏</string>
|
||||
<string name="noBarcode">无条形码</string>
|
||||
<string name="barcodeNoBarcode">无条形码</string>
|
||||
<string name="barcodeNoBarcode">无可用条形码</string>
|
||||
<string name="barcodeType">条形码类型</string>
|
||||
<string name="cardId">卡号</string>
|
||||
<string name="note">备注</string>
|
||||
<string name="storeName">名称</string>
|
||||
<string name="noMatchingGiftCards">没有结果。尝试改变你的搜索。</string>
|
||||
<string name="noGiftCards">点击 \"+\"加号按钮来添加卡片,或者从⋮ 菜单导入。</string>
|
||||
<string name="noMatchingGiftCards">没有结果。试试其他关键字。</string>
|
||||
<string name="noGiftCards">点击“+”加号按钮来添加卡片,或者从 ⋮ 菜单导入。</string>
|
||||
<string name="action_add">添加</string>
|
||||
<string name="action_search">搜索</string>
|
||||
<string name="deleteConfirmation">删除此卡?</string>
|
||||
<string name="deleteConfirmation">确定要永久删除此卡?</string>
|
||||
<string name="deleteTitle">移除卡片</string>
|
||||
<string name="starImage">最喜欢的星星</string>
|
||||
<string name="starImage">收藏夹星星</string>
|
||||
<string name="importStocardMessage">选择 Stocard 导出文件 <i>****.zip</i>来导入。
|
||||
\n发电子邮件给 support@stocardapp.com 请求获得数据导出文件。</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="other">确定永久删除 <xliff:g>%d</xliff:g> 这些卡片?</item>
|
||||
<item quantity="other">确定要永久删除 <xliff:g>%d</xliff:g> 这些卡片?</item>
|
||||
</plurals>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="other">选中了 <xliff:g>%d</xliff:g> 张卡</item>
|
||||
@@ -163,14 +162,14 @@
|
||||
<string name="backImageDescription">背面图像</string>
|
||||
<string name="frontImageDescription">正面图像</string>
|
||||
<string name="importStocard">从Stocard导入</string>
|
||||
<string name="noGiftCardsGroup">创建一些卡片,然后分配给这里的小组。</string>
|
||||
<string name="noGiftCardsGroup">创建一些卡片并分配到这个卡组中。</string>
|
||||
<string name="barcodeImageDescriptionWithType"><xliff:g>%s</xliff:g>型条码图像</string>
|
||||
<string name="group_edit">编辑组</string>
|
||||
<plurals name="balancePoints">
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> 点</item>
|
||||
</plurals>
|
||||
<string name="settings_locale">语言</string>
|
||||
<string name="setIcon">设置图标</string>
|
||||
<string name="setIcon">设置缩略图</string>
|
||||
<string name="settings_pink_theme">粉色</string>
|
||||
<string name="settings_oled_dark">纯黑色背景的深色主题</string>
|
||||
<string name="translate_platform">在Weblate上</string>
|
||||
@@ -247,12 +246,12 @@
|
||||
<string name="failedToRetrieveImageFile">无法检索图像文件</string>
|
||||
<string name="newBalanceSentence">新余额:<xliff:g>%s</xliff:g></string>
|
||||
<string name="barcodeLongPressMessage">只能在图库应用程序中打开图像</string>
|
||||
<string name="storageReadPermissionRequired">此操作所需的读取存储空间的权限…</string>
|
||||
<string name="storageReadPermissionRequired">此操作所需的读取存储空间的权限……</string>
|
||||
<string name="validFromDate">有效期自</string>
|
||||
<string name="anyDate">任何日期</string>
|
||||
<string name="chooseValidFromDate">选择有效日期</string>
|
||||
<string name="validFromSentence">有效期自: <xliff:g>%s</xliff:g></string>
|
||||
<string name="cameraPermissionRequired">此操作所需的访问相机的权限…</string>
|
||||
<string name="cameraPermissionRequired">此操作所需的访问相机的权限……</string>
|
||||
<string name="height">高度:</string>
|
||||
<string name="switchToFrontImage">选择正面图像</string>
|
||||
<string name="switchToBackImage">选择背面图像</string>
|
||||
@@ -261,4 +260,20 @@
|
||||
<string name="openBackImageInGalleryApp">使用图库软件打开背面图像</string>
|
||||
<string name="setBarcodeHeight">设置条形码高度</string>
|
||||
<string name="donate">捐赠</string>
|
||||
<string name="icon_header_click_text">长按可编辑缩略图</string>
|
||||
<string name="show_note">显示备注</string>
|
||||
<string name="show_balance">显示余额</string>
|
||||
<string name="show_name_below_image_thumbnail">在图像缩略图下方显示名称</string>
|
||||
<string name="show_validity">显示有效性</string>
|
||||
<string name="permissionReadCardsLabel">读取 Catima 卡</string>
|
||||
<string name="settings_allow_content_provider_read_summary">应用程序仍然需要请求权限</string>
|
||||
<string name="settings_allow_content_provider_read_title">允许其他应用程序访问我的数据</string>
|
||||
<string name="permissionReadCardsDescription">读取你的卡片及其所有细节,包括笔记和图像</string>
|
||||
<string name="settings_keep_screen_on_summary">在查看卡片时禁止屏幕超时</string>
|
||||
<string name="settings_oled_dark_summary">减少 OLED 显示屏上的电池使用量</string>
|
||||
<string name="settings_category_title_privacy">隐私</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">查看卡片时禁用屏幕锁</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">一些扫描仪工作所必需的</string>
|
||||
<string name="settings_category_title_cards">卡包</string>
|
||||
<string name="settings_category_title_general">常规</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@
|
||||
\n
|
||||
\n我們並不會收集任何資料!任何人都可以檢視我們的原始碼並驗證這點。</string>
|
||||
<string name="star">新增至收藏</string>
|
||||
<string name="app_license">公共版權 (Copylefted) 的自由軟體,許可 GPLv3+</string>
|
||||
<string name="app_license">公共版權(Copylefted)的自由軟體,許可 GPLv3+</string>
|
||||
<string name="unstar">從收藏中移除</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="save">儲存</string>
|
||||
@@ -29,12 +29,12 @@
|
||||
<string name="addManually">手動輸入 ID</string>
|
||||
<string name="all">全部</string>
|
||||
<string name="balance">餘額</string>
|
||||
<string name="balanceSentence">餘額: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balanceSentence">餘額:<xliff:g>%s</xliff:g></string>
|
||||
<string name="app_copyright_old">基於 Loyalty Card Keychain\n著作權所有 © 2016–2020 Branden Archer</string>
|
||||
<string name="barcodeId">條碼內容</string>
|
||||
<string name="barcodeImageDescriptionWithType">圖片為 <xliff:g>%s</xliff:g> 的條碼</string>
|
||||
<string name="card">卡片</string>
|
||||
<string name="card_ids_copied">已複製 ID(s)</string>
|
||||
<string name="card_ids_copied">已複製 ID</string>
|
||||
<string name="cardShortcut">卡片捷徑</string>
|
||||
<string name="chooseExpiryDate">選擇逾期日期</string>
|
||||
<string name="chooseImportType">從地點匯入資料</string>
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="exporting">匯出中…</string>
|
||||
<string name="exportName">匯出</string>
|
||||
<string name="exportOptionExplanation">資料將寫至您所選的位置。</string>
|
||||
<string name="exportPassword">透過密碼保護您的匯出檔 (選用)</string>
|
||||
<string name="exportPassword">透過密碼保護您的匯出檔(選用)</string>
|
||||
<string name="exportPasswordHint">輸入密碼</string>
|
||||
<string name="exportSuccessful">已匯出資料</string>
|
||||
<string name="exportSuccessfulTitle">已匯出</string>
|
||||
@@ -70,14 +70,14 @@
|
||||
<string name="groups">群組</string>
|
||||
<string name="groupsList">群組:<xliff:g>%s</xliff:g></string>
|
||||
<string name="help_translate_this_app">幫助翻譯本程式</string>
|
||||
<string name="importExport">匯入/匯出</string>
|
||||
<string name="importExport">匯入/匯出</string>
|
||||
<string name="importFailedTitle">匯入失敗</string>
|
||||
<string name="importFailed">無法匯入資料</string>
|
||||
<string name="importing">匯入中…</string>
|
||||
<string name="importSuccessfulTitle">已匯入</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">我想要分享些卡片給你</string>
|
||||
<string name="intent_import_card_from_url_share_text">我想要分享張卡片給你</string>
|
||||
<string name="leaveWithoutSaveConfirmation">不保存就離開?</string>
|
||||
<string name="leaveWithoutSaveConfirmation">不儲存就離開?</string>
|
||||
<string name="leaveWithoutSaveTitle">離開</string>
|
||||
<string name="license">許可</string>
|
||||
<string name="never">永不</string>
|
||||
@@ -123,7 +123,7 @@
|
||||
<string name="settings_locale">語言</string>
|
||||
<string name="settings_system_locale">系統語言</string>
|
||||
<string name="settings_theme_color">主題顏色</string>
|
||||
<string name="app_contributors">感謝以下貢獻者: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="app_contributors">感謝以下貢獻者:<xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="privacy_policy">隱私權政策</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="other">已選取 <xliff:g>%d</xliff:g></item>
|
||||
@@ -132,7 +132,7 @@
|
||||
<item quantity="other">刪除 <xliff:g>%d</xliff:g> 張卡片</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="other">永久刪除 <xliff:g>%d</xliff:g> 張卡片?</item>
|
||||
<item quantity="other">永久刪除 <xliff:g>%d</xliff:g> 張卡片?</item>
|
||||
</plurals>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> 張卡片</item>
|
||||
@@ -143,7 +143,6 @@
|
||||
<string name="settings_theme">主題</string>
|
||||
<string name="thumbnailDescription">縮圖</string>
|
||||
<string name="noGroupCards">此群組為空</string>
|
||||
<string name="settings_category_title_ui">用戶界面</string>
|
||||
<string name="settings_light_theme">淺色</string>
|
||||
<string name="settings_dark_theme">深色</string>
|
||||
<string name="settings_display_barcode_max_brightness">調高條碼介面螢幕亮度</string>
|
||||
@@ -178,10 +177,10 @@
|
||||
\n請您先透過您的 FidMe 個人檔案選取『Data Protection』,並選擇『Extract my data』。</string>
|
||||
<string name="importCatima">自 Catima 匯入</string>
|
||||
<string name="importCatimaMessage">選取您自 Catima 匯出的 <i>catima.zip</i> 檔案以進行匯入。
|
||||
\n您可透過其他裝置的 Catima 程式中的匯入/匯出選單進行匯出。</string>
|
||||
\n您可透過其他裝置的 Catima 程式中的匯入/匯出選單進行匯出。</string>
|
||||
<string name="points">點</string>
|
||||
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> 似乎不是個可用的餘額數值。</string>
|
||||
<string name="app_libraries">第三方自由函示庫:<xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_libraries">第三方自由函式庫:<xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="app_resources">第三方自由資源:<xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="selectBarcodeTitle">選擇條碼</string>
|
||||
<string name="noGroups">請點選 + 加號按鈕新增群組。</string>
|
||||
@@ -202,7 +201,7 @@
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> 個點數</item>
|
||||
</plurals>
|
||||
<string name="selectColor">選擇顏色</string>
|
||||
<string name="setIcon">設定圖示</string>
|
||||
<string name="setIcon">設定縮圖</string>
|
||||
<string name="group_edit">編輯群組</string>
|
||||
<string name="group_name_already_in_use">此群組名稱已存在</string>
|
||||
<string name="group_name_is_empty">群組名稱不能為空</string>
|
||||
@@ -235,7 +234,7 @@
|
||||
<string name="nextCard">下一張</string>
|
||||
<string name="welcome">歡迎使用 Catima</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="other">檢視封存 (<xliff:g>%1$d</xliff:g> 張卡片)</item>
|
||||
<item quantity="other">檢視封存(<xliff:g>%1$d</xliff:g> 張卡片)</item>
|
||||
</plurals>
|
||||
<string name="settings_lock_on_opening_orientation">開啟卡片時鎖定的方向</string>
|
||||
<string name="importCards">導入卡片</string>
|
||||
@@ -243,13 +242,26 @@
|
||||
<string name="validFromDate">生效</string>
|
||||
<string name="anyDate">任何日期</string>
|
||||
<string name="chooseValidFromDate">選擇生效日期</string>
|
||||
<string name="validFromSentence">生效日期: <xliff:g>%s</xliff:g></string>
|
||||
<string name="validFromSentence">生效日期:<xliff:g>%s</xliff:g></string>
|
||||
<string name="storageReadPermissionRequired">這個行動需要閱讀儲存空間的權限…</string>
|
||||
<string name="cameraPermissionRequired">這個行動需要使用鏡頭的權限…</string>
|
||||
<string name="cameraPermissionDeniedTitle">不能使用鏡頭</string>
|
||||
<string name="updateBalance">更新餘額</string>
|
||||
<string name="updateBalanceTitle">你用了多少?</string>
|
||||
<string name="updateBalanceHint">輸入金額</string>
|
||||
<string name="currentBalanceSentence">餘額: <xliff:g>%s</xliff:g></string>
|
||||
<string name="newBalanceSentence">新的餘額: <xliff:g>%s</xliff:g></string>
|
||||
</resources>
|
||||
<string name="currentBalanceSentence">餘額:<xliff:g>%s</xliff:g></string>
|
||||
<string name="newBalanceSentence">新的餘額:<xliff:g>%s</xliff:g></string>
|
||||
<string name="switchToBarcode">選擇條碼</string>
|
||||
<string name="openFrontImageInGalleryApp">以圖庫軟件開啟正面圖片</string>
|
||||
<string name="show_note">顯示備註</string>
|
||||
<string name="show_balance">顯示餘額</string>
|
||||
<string name="show_validity">顯示有效性</string>
|
||||
<string name="switchToFrontImage">選擇正面圖片</string>
|
||||
<string name="switchToBackImage">選擇背面圖片</string>
|
||||
<string name="height">高度:</string>
|
||||
<string name="donate">捐款</string>
|
||||
<string name="icon_header_click_text">長按以編輯縮圖</string>
|
||||
<string name="openBackImageInGalleryApp">以圖庫軟體開啟背面圖片</string>
|
||||
<string name="show_name_below_image_thumbnail">在縮圖下方顯示名稱</string>
|
||||
<string name="setBarcodeHeight">設定條碼高度</string>
|
||||
</resources>
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
<string name="exporting">Exporting…</string>
|
||||
<string name="storageReadPermissionRequired">Permission to read storage needed for this action…</string>
|
||||
<string name="cameraPermissionRequired">Permission to access camera needed for this action…</string>
|
||||
<string name="permissionReadCardsLabel">Read Catima Cards</string>
|
||||
<string name="permissionReadCardsDescription">Read your cards and all its details, including notes and images</string>
|
||||
<string name="cameraPermissionDeniedTitle">Could not access the camera</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">To scan barcodes, Catima will need access to your camera. Tap here to change your permission settings.</string>
|
||||
<string name="exportOptionExplanation">The data will be written to a location of your choice.</string>
|
||||
@@ -90,7 +92,6 @@
|
||||
<string name="thumbnailDescription">Thumbnail</string>
|
||||
<string name="starImage">Favorite star</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="settings_category_title_ui">User interface</string>
|
||||
<string name="settings_theme">Theme</string>
|
||||
<string name="settings_key_theme" translatable="false">pref_theme</string>
|
||||
<string name="settings_system_theme">System</string>
|
||||
@@ -111,11 +112,17 @@
|
||||
<string name="settings_key_lock_on_opening_orientation" translatable="false">lock_on_opening</string>
|
||||
<string name="settings_key_max_font_size_scale" translatable="false">pref_max_font_size_scale</string>
|
||||
<string name="settings_display_barcode_max_brightness">Brighten barcode view</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Necessary for some scanners to work</string>
|
||||
<string name="settings_key_display_barcode_max_brightness" translatable="false">pref_display_card_max_brightness</string>
|
||||
<string name="settings_keep_screen_on">Keep screen on</string>
|
||||
<string name="settings_keep_screen_on_summary">Disables screen timeout while viewing a card</string>
|
||||
<string name="settings_key_keep_screen_on" translatable="false">pref_keep_screen_on</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Prevent screen lock</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Disables screen lock while viewing a card</string>
|
||||
<string name="settings_allow_content_provider_read_title">Allow other apps to access my data</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Apps will still have to request permission to be granted access</string>
|
||||
<string name="settings_key_disable_lockscreen_while_viewing_card" translatable="false">pref_disable_lockscreen_while_viewing_card</string>
|
||||
<string name="settings_key_allow_content_provider_read" translatable="false">pref_allow_content_provider_read</string>
|
||||
<string name="settings_key_oled_dark" translatable="false">pref_oled_dark</string>
|
||||
<string name="sharedpreference_active_tab" translatable="false">sharedpreference_active_tab</string>
|
||||
<string name="sharedpreference_privacy_policy_shown" translatable="false">sharedpreference_privacy_policy_shown</string>
|
||||
@@ -123,7 +130,6 @@
|
||||
<string name="sharedpreference_sort_order" translatable="false">sharedpreference_sort_order</string>
|
||||
<string name="sharedpreference_sort_direction" translatable="false">sharedpreference_sort_direction</string>
|
||||
<string name="sharedpreference_card_details" translatable="false">sharedpreference_card_details</string>
|
||||
<string name="sharedpreference_card_details_show" translatable="false">sharedpreference_card_details_show</string>
|
||||
<string name="intent_import_card_from_url_share_text">I want to share a card with you</string>
|
||||
<string name="intent_import_card_from_url_host_catima_app" translatable="false">catima.app</string>
|
||||
<string name="intent_import_card_from_url_path_prefix_catima_app" translatable="false">/share</string>
|
||||
@@ -221,10 +227,11 @@
|
||||
<string name="turn_flashlight_off">Turn flashlight off</string>
|
||||
<string name="settings_locale">Language</string>
|
||||
<string name="settings_oled_dark">Pure black background for dark theme</string>
|
||||
<string name="settings_oled_dark_summary">Reduces battery usage on OLED displays</string>
|
||||
<string name="settings_key_locale" translatable="false">pref_locale</string>
|
||||
<string name="settings_system_locale">System</string>
|
||||
<string name="selectColor">Select color</string>
|
||||
<string name="setIcon">Set icon</string>
|
||||
<string name="setIcon">Set thumbnail</string>
|
||||
<string name="setting_key_theme_color" translatable="false">pref_theme_color</string>
|
||||
<string name="settings_theme_color">Theme color</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
@@ -313,4 +320,16 @@
|
||||
<string name="openBackImageInGalleryApp">Open back image in gallery app</string>
|
||||
<string name="setBarcodeHeight">Set barcode height</string>
|
||||
<string name="donate">Donate</string>
|
||||
<string name="icon_header_click_text">Long press to edit thumbnail</string>
|
||||
<string name="show_name_below_image_thumbnail">Show name below image thumbnail</string>
|
||||
<string name="show_note">Show note</string>
|
||||
<string name="show_balance">Show balance</string>
|
||||
<string name="show_validity">Show validity</string>
|
||||
<string name="sharedpreference_card_details_show_note" translatable="false">sharedpreference_card_details_show_note</string>
|
||||
<string name="sharedpreference_card_details_show_balance" translatable="false">sharedpreference_card_details_show_balance</string>
|
||||
<string name="sharedpreference_card_details_show_validity" translatable="false">sharedpreference_card_details_show_validity</string>
|
||||
<string name="sharedpreference_card_details_show_name_below_thumbnail" translatable="false">sharedpreference_card_details_show_name_below_thumbnail</string>
|
||||
<string name="settings_category_title_cards">Cards</string>
|
||||
<string name="settings_category_title_general">General</string>
|
||||
<string name="settings_category_title_privacy">Privacy</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_category_title_ui"
|
||||
android:title="@string/settings_category_title_general"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<ListPreference
|
||||
@@ -13,7 +13,8 @@
|
||||
android:key="@string/settings_key_theme"
|
||||
android:title="@string/settings_theme"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
app:singleLineTitle="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
android:key="@string/setting_key_theme_color"
|
||||
@@ -22,12 +23,14 @@
|
||||
android:entries="@array/color_value_strings"
|
||||
android:entryValues="@array/color_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
app:singleLineTitle="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preference_material_switch"
|
||||
android:defaultValue="false"
|
||||
android:key="@string/settings_key_oled_dark"
|
||||
android:summary="@string/settings_oled_dark_summary"
|
||||
android:title="@string/settings_oled_dark"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
@@ -39,12 +42,19 @@
|
||||
android:key="@string/settings_key_locale"
|
||||
android:title="@string/settings_locale"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
app:singleLineTitle="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_category_title_cards"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preference_material_switch"
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_display_barcode_max_brightness"
|
||||
android:summary="@string/settings_display_barcode_max_brightness_summary"
|
||||
android:title="@string/settings_display_barcode_max_brightness"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
@@ -56,12 +66,14 @@
|
||||
android:key="@string/settings_key_card_orientation"
|
||||
android:title="@string/settings_card_orientation"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
app:singleLineTitle="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preference_material_switch"
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_keep_screen_on"
|
||||
android:summary="@string/settings_keep_screen_on_summary"
|
||||
android:title="@string/settings_keep_screen_on"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
@@ -70,11 +82,25 @@
|
||||
android:widgetLayout="@layout/preference_material_switch"
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_disable_lockscreen_while_viewing_card"
|
||||
android:summary="@string/settings_disable_lockscreen_while_viewing_card_summary"
|
||||
android:title="@string/settings_disable_lockscreen_while_viewing_card"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_category_title_privacy"
|
||||
app:iconSpaceReserved="false">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preference_material_switch"
|
||||
android:defaultValue="true"
|
||||
android:key="@string/settings_key_allow_content_provider_read"
|
||||
android:summary="@string/settings_allow_content_provider_read_summary"
|
||||
android:title="@string/settings_allow_content_provider_read_title"
|
||||
app:iconSpaceReserved="false"
|
||||
app:singleLineTitle="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
|
||||
@@ -67,25 +67,6 @@ public class ImportExportTest {
|
||||
mDatabase = TestHelpers.getEmptyDb(activity).getWritableDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given number of cards, each with
|
||||
* an index in the store name.
|
||||
*
|
||||
* @param cardsToAdd
|
||||
*/
|
||||
private void addLoyaltyCards(int cardsToAdd) {
|
||||
// Add in reverse order to test sorting
|
||||
for (int index = cardsToAdd; index > 0; index--) {
|
||||
String storeName = String.format("store, \"%4d", index);
|
||||
String note = String.format("note, \"%4d", index);
|
||||
long id = DBHelper.insertLoyaltyCard(mDatabase, storeName, note, null, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, null, BARCODE_TYPE, index, 0, null,0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
assertEquals(cardsToAdd, DBHelper.getLoyaltyCardCount(mDatabase));
|
||||
}
|
||||
|
||||
private void addLoyaltyCardsFiveStarred() {
|
||||
int cardsToAdd = 9;
|
||||
// Add in reverse order to test sorting
|
||||
@@ -183,18 +164,6 @@ public class ImportExportTest {
|
||||
assertEquals(4, DBHelper.getLoyaltyCardCount(mDatabase));
|
||||
}
|
||||
|
||||
private void addGroups(int groupsToAdd) {
|
||||
// Add in reverse order to test sorting
|
||||
for (int index = groupsToAdd; index > 0; index--) {
|
||||
String groupName = String.format("group, \"%4d", index);
|
||||
long id = DBHelper.insertGroup(mDatabase, groupName);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
assertEquals(groupsToAdd, DBHelper.getGroupCount(mDatabase));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all of the cards follow the pattern
|
||||
* specified in addLoyaltyCards(), and are in sequential order
|
||||
@@ -227,6 +196,35 @@ public class ImportExportTest {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
private void checkLoyaltyCardsAndDuplicates(int numCards) {
|
||||
Cursor cursor = DBHelper.getLoyaltyCardCursor(mDatabase);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cursor);
|
||||
|
||||
// ID goes up for duplicates (b/c the cursor orders by store), down for originals
|
||||
int index = card.id > numCards ? card.id - numCards : numCards - card.id + 1;
|
||||
// balance is doubled for modified originals
|
||||
int balance = card.id > numCards ? index : index * 2;
|
||||
|
||||
String expectedStore = String.format("store, \"%4d", index);
|
||||
String expectedNote = String.format("note, \"%4d", index);
|
||||
|
||||
assertEquals(expectedStore, card.store);
|
||||
assertEquals(expectedNote, card.note);
|
||||
assertEquals(null, card.validFrom);
|
||||
assertEquals(null, card.expiry);
|
||||
assertEquals(new BigDecimal(String.valueOf(balance)), card.balance);
|
||||
assertEquals(null, card.balanceType);
|
||||
assertEquals(BARCODE_DATA, card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BARCODE_TYPE.format(), card.barcodeType.format());
|
||||
assertEquals(Integer.valueOf(index), card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all of the cards follow the pattern
|
||||
* specified in addLoyaltyCardsSomeStarred(), and are in sequential order
|
||||
@@ -285,7 +283,7 @@ public class ImportExportTest {
|
||||
|
||||
/**
|
||||
* Check that all of the groups follow the pattern
|
||||
* specified in addGroups(), and are in sequential order
|
||||
* specified in {@link TestHelpers#addGroups}, and are in sequential order
|
||||
* where the smallest group's index is 1
|
||||
*/
|
||||
private void checkGroups() {
|
||||
@@ -308,7 +306,7 @@ public class ImportExportTest {
|
||||
public void multipleCardsExportImport() throws IOException {
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
@@ -338,7 +336,7 @@ public class ImportExportTest {
|
||||
final int NUM_CARDS = 10;
|
||||
List<char[]> passwords = Arrays.asList(null, "123456789".toCharArray());
|
||||
for (char[] password : passwords) {
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
@@ -411,8 +409,8 @@ public class ImportExportTest {
|
||||
final int NUM_CARDS = 10;
|
||||
final int NUM_GROUPS = 3;
|
||||
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
addGroups(NUM_GROUPS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
TestHelpers.addGroups(mDatabase, NUM_GROUPS);
|
||||
|
||||
List<Group> emptyGroup = new ArrayList<>();
|
||||
|
||||
@@ -484,7 +482,7 @@ public class ImportExportTest {
|
||||
public void importExistingCardsNotReplace() throws IOException {
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
@@ -508,12 +506,46 @@ public class ImportExportTest {
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importExistingCardsAfterModification() throws IOException {
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
|
||||
// Export into CSV data
|
||||
ImportExportResult result = MultiFormatExporter.exportData(activity.getApplicationContext(), mDatabase, outData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResultType.Success, result.resultType());
|
||||
outStream.close();
|
||||
|
||||
// Modify existing cards
|
||||
for (int index = 1; index <= NUM_CARDS; index++) {
|
||||
int id = NUM_CARDS - index + 1;
|
||||
DBHelper.updateLoyaltyCardBalance(mDatabase, id, new BigDecimal(String.valueOf(index * 2)));
|
||||
}
|
||||
|
||||
ByteArrayInputStream inData = new ByteArrayInputStream(outData.toByteArray());
|
||||
|
||||
// Import the CSV data on top of the existing database
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), mDatabase, inData, DataFormat.Catima, null);
|
||||
assertEquals(ImportExportResultType.Success, result.resultType());
|
||||
|
||||
assertEquals(NUM_CARDS * 2, DBHelper.getLoyaltyCardCount(mDatabase));
|
||||
|
||||
checkLoyaltyCardsAndDuplicates(NUM_CARDS);
|
||||
|
||||
// Clear the database for the next format under test
|
||||
TestHelpers.getEmptyDb(activity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void corruptedImportNothingSaved() {
|
||||
final int NUM_CARDS = 10;
|
||||
|
||||
for (DataFormat format : DataFormat.values()) {
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
ByteArrayOutputStream outData = new ByteArrayOutputStream();
|
||||
OutputStreamWriter outStream = new OutputStreamWriter(outData);
|
||||
@@ -558,7 +590,7 @@ public class ImportExportTest {
|
||||
final File sdcardDir = Environment.getExternalStorageDirectory();
|
||||
final File exportFile = new File(sdcardDir, "Catima.csv");
|
||||
|
||||
addLoyaltyCards(NUM_CARDS);
|
||||
TestHelpers.addLoyaltyCards(mDatabase, NUM_CARDS);
|
||||
|
||||
TestTaskCompleteListener listener = new TestTaskCompleteListener();
|
||||
|
||||
@@ -786,8 +818,9 @@ public class ImportExportTest {
|
||||
@Test
|
||||
public void exportImportV2Zip() throws FileNotFoundException {
|
||||
// Prepare images
|
||||
Bitmap bitmap1 = new LetterBitmap(activity.getApplicationContext(), "1", "1", 12, 64, 64, Color.BLACK, Color.YELLOW).getLetterTile();
|
||||
Bitmap bitmap2 = new LetterBitmap(activity.getApplicationContext(), "2", "2", 12, 64, 64, Color.GREEN, Color.WHITE).getLetterTile();
|
||||
// NB: we can't use LetterBitmap as robolectric doesn't support Canvas enough
|
||||
Bitmap bitmap1 = BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg"));
|
||||
Bitmap bitmap2 = BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg"));
|
||||
|
||||
// Set up cards and groups
|
||||
HashMap<Integer, LoyaltyCard> loyaltyCardHashMap = new HashMap<>();
|
||||
@@ -1092,10 +1125,6 @@ public class ImportExportTest {
|
||||
|
||||
@Test
|
||||
public void importStocard() {
|
||||
// FIXME: The provided stocard.zip is a very old export (8 July 2021) manually edited to
|
||||
// look more like the Stocard files provided by users for #1242. It is not an up-to-date
|
||||
// export and the test is possibly unreliable. This should be replaced by an up-to-date
|
||||
// export.
|
||||
InputStream inputStream = getClass().getResourceAsStream("stocard.zip");
|
||||
|
||||
// Import the Stocard data
|
||||
@@ -1105,7 +1134,7 @@ public class ImportExportTest {
|
||||
|
||||
inputStream = getClass().getResourceAsStream("stocard.zip");
|
||||
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), mDatabase, inputStream, DataFormat.Stocard, "da811b40a4dac56f0cbb2d99b21bbb9a".toCharArray());
|
||||
result = MultiFormatImporter.importData(activity.getApplicationContext(), mDatabase, inputStream, DataFormat.Stocard, "sE0p0RiFDteqhlD4adwWpwjvmI0r0CFOTfyzRae4vEsgNe3NKL".toCharArray());
|
||||
assertEquals(ImportExportResultType.Success, result.resultType());
|
||||
assertEquals(3, DBHelper.getLoyaltyCardCount(mDatabase));
|
||||
|
||||
@@ -1121,6 +1150,7 @@ public class ImportExportTest {
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(1625600883, card.lastUsed);
|
||||
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front));
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back));
|
||||
@@ -1138,6 +1168,7 @@ public class ImportExportTest {
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(1625690099, card.lastUsed);
|
||||
|
||||
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front)));
|
||||
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back)));
|
||||
@@ -1155,6 +1186,7 @@ public class ImportExportTest {
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.RSS_EXPANDED, card.barcodeType.format());
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(1625600120, card.lastUsed);
|
||||
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.front));
|
||||
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.back));
|
||||
|
||||
@@ -97,6 +97,37 @@ public class ImportURITest {
|
||||
assertEquals(0, parsedCard.archiveStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseWithTrailingSlash() throws InvalidObjectException, UnsupportedEncodingException {
|
||||
// Generate card
|
||||
DBHelper.insertLoyaltyCard(mDatabase, "store", "note", null, null, new BigDecimal("10.00"), Currency.getInstance("EUR"), BarcodeFormat.UPC_A.toString(), null, CatimaBarcode.fromBarcode(BarcodeFormat.QR_CODE), null, 0, null,0);
|
||||
|
||||
// Get card
|
||||
LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, 1);
|
||||
|
||||
// Card to URI, with a trailing slash
|
||||
Uri cardUri = importURIHelper.toUri(card).buildUpon().path("/share/").build();
|
||||
assertEquals("/share/", cardUri.getPath());
|
||||
|
||||
// Parse URI
|
||||
LoyaltyCard parsedCard = importURIHelper.parse(cardUri);
|
||||
|
||||
// Compare everything
|
||||
assertEquals(card.store, parsedCard.store);
|
||||
assertEquals(card.note, parsedCard.note);
|
||||
assertEquals(card.validFrom, parsedCard.validFrom);
|
||||
assertEquals(card.expiry, parsedCard.expiry);
|
||||
assertEquals(card.balance, parsedCard.balance);
|
||||
assertEquals(card.balanceType, parsedCard.balanceType);
|
||||
assertEquals(card.cardId, parsedCard.cardId);
|
||||
assertEquals(card.barcodeId, parsedCard.barcodeId);
|
||||
assertEquals(card.barcodeType.format(), parsedCard.barcodeType.format());
|
||||
assertNull(parsedCard.headerColor);
|
||||
// No export of starStatus for export URL foreseen therefore 0 will be imported
|
||||
assertEquals(0, parsedCard.starStatus);
|
||||
assertEquals(0, parsedCard.archiveStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failToParseInvalidUri() {
|
||||
try {
|
||||
|
||||
@@ -62,7 +62,7 @@ public class MainActivityTest {
|
||||
assertEquals(menu.size(), 8);
|
||||
assertEquals("Search", menu.findItem(R.id.action_search).getTitle().toString());
|
||||
assertEquals("Sort", menu.findItem(R.id.action_sort).getTitle().toString());
|
||||
assertEquals("Hide details", menu.findItem(R.id.action_unfold).getTitle().toString());
|
||||
assertEquals("Show details", menu.findItem(R.id.action_shown_details).getTitle().toString());
|
||||
assertEquals("Groups", menu.findItem(R.id.action_manage_groups).getTitle().toString());
|
||||
assertEquals("Archive", menu.findItem(R.id.action_archived).getTitle().toString());
|
||||
assertEquals("Import/Export", menu.findItem(R.id.action_import_export).getTitle().toString());
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.app.Activity;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class TestHelpers {
|
||||
static public DBHelper getEmptyDb(Activity activity) {
|
||||
DBHelper db = new DBHelper(activity);
|
||||
private static final String BARCODE_DATA = "428311627547";
|
||||
private static final CatimaBarcode BARCODE_TYPE = CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A);
|
||||
|
||||
public static DBHelper getEmptyDb(Context context) {
|
||||
DBHelper db = new DBHelper(context);
|
||||
SQLiteDatabase database = db.getWritableDatabase();
|
||||
|
||||
// Make sure no files remain
|
||||
@@ -19,7 +28,7 @@ public class TestHelpers {
|
||||
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
try {
|
||||
Utils.saveCardImage(activity.getApplicationContext(), null, cardID, imageLocationType);
|
||||
Utils.saveCardImage(context.getApplicationContext(), null, cardID, imageLocationType);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
@@ -34,4 +43,35 @@ public class TestHelpers {
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given number of cards, each with an index in the store name.
|
||||
*
|
||||
* @param mDatabase
|
||||
* @param cardsToAdd
|
||||
*/
|
||||
public static void addLoyaltyCards(final SQLiteDatabase mDatabase, final int cardsToAdd) {
|
||||
// Add in reverse order to test sorting
|
||||
for (int index = cardsToAdd; index > 0; index--) {
|
||||
String storeName = String.format("store, \"%4d", index);
|
||||
String note = String.format("note, \"%4d", index);
|
||||
long id = DBHelper.insertLoyaltyCard(mDatabase, storeName, note, null, null, new BigDecimal(String.valueOf(index)), null, BARCODE_DATA, null, BARCODE_TYPE, index, 0, null,0);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
assertEquals(cardsToAdd, DBHelper.getLoyaltyCardCount(mDatabase));
|
||||
}
|
||||
|
||||
public static void addGroups(final SQLiteDatabase mDatabase, int groupsToAdd) {
|
||||
// Add in reverse order to test sorting
|
||||
for (int index = groupsToAdd; index > 0; index--) {
|
||||
String groupName = String.format("group, \"%4d", index);
|
||||
long id = DBHelper.insertGroup(mDatabase, groupName);
|
||||
boolean result = (id != -1);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
assertEquals(groupsToAdd, DBHelper.getGroupCount(mDatabase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
package protect.card_locker.contentprovider;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import protect.card_locker.CatimaBarcode;
|
||||
import protect.card_locker.DBHelper;
|
||||
import protect.card_locker.Group;
|
||||
import protect.card_locker.TestHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class CardsContentProviderTest {
|
||||
private ContentResolver mResolver;
|
||||
private DBHelper dbHelper;
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final ContentProvider contentProvider = new CardsContentProvider();
|
||||
final ProviderInfo providerInfo = new ProviderInfo();
|
||||
providerInfo.authority = CardsContentProvider.AUTHORITY;
|
||||
contentProvider.attachInfo(RuntimeEnvironment.getApplication(), providerInfo);
|
||||
contentProvider.onCreate();
|
||||
Robolectric.buildContentProvider(CardsContentProvider.class).create(providerInfo);
|
||||
|
||||
mResolver = RuntimeEnvironment.getApplication().getContentResolver();
|
||||
dbHelper = TestHelpers.getEmptyDb(RuntimeEnvironment.getApplication());
|
||||
mDatabase = dbHelper.getWritableDatabase();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
mDatabase.close();
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersion() {
|
||||
final Uri versionUri = getUri("version");
|
||||
|
||||
try (Cursor cursor = mResolver.query(versionUri, null, null, null)) {
|
||||
assertEquals("number of entries", 1, cursor.getCount());
|
||||
assertEquals("number of columns", 2, cursor.getColumnCount());
|
||||
assertArrayEquals("column names", new String[]{"major", "minor"}, cursor.getColumnNames());
|
||||
cursor.moveToNext();
|
||||
assertEquals("major version", 1, cursor.getInt(cursor.getColumnIndexOrThrow("major")));
|
||||
assertEquals("minor version", 0, cursor.getInt(cursor.getColumnIndexOrThrow("minor")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCards() {
|
||||
final Uri cardsUri = getUri("cards");
|
||||
|
||||
try (Cursor cursor = mResolver.query(cardsUri, null, null, null)) {
|
||||
assertEquals(cursor.getCount(), 0);
|
||||
}
|
||||
|
||||
final String store = "the best store";
|
||||
final String note = "this is a note";
|
||||
final Date validFrom = Date.from(Instant.ofEpochMilli(1687112209000L));
|
||||
final Date expiry = Date.from(Instant.ofEpochMilli(1687112277000L));
|
||||
final BigDecimal balance = new BigDecimal("123.20");
|
||||
final Currency balanceType = Currency.getInstance("EUR");
|
||||
final String cardId = "a-card-id";
|
||||
final String barcodeId = "barcode-id";
|
||||
final CatimaBarcode barcodeType = CatimaBarcode.fromName("QR_CODE");
|
||||
final int headerColor = 0xFFFF00FF;
|
||||
final int starStatus = 1;
|
||||
final long lastUsed = 1687112282000L;
|
||||
final int archiveStatus = 1;
|
||||
long id = DBHelper.insertLoyaltyCard(
|
||||
mDatabase, store, note, validFrom, expiry, balance, balanceType,
|
||||
cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed,
|
||||
archiveStatus
|
||||
);
|
||||
assertEquals("expect first card", 1, id);
|
||||
|
||||
try (Cursor cursor = mResolver.query(cardsUri, null, null, null)) {
|
||||
assertEquals("number of cards", 1, cursor.getCount());
|
||||
|
||||
final String[] expectedColumns = new String[]{
|
||||
"_id", "store", "validfrom", "expiry", "balance", "balancetype",
|
||||
"note", "headercolor", "cardid", "barcodeid",
|
||||
"barcodetype", "starstatus", "lastused", "archive"
|
||||
};
|
||||
|
||||
assertEquals("number of columns", expectedColumns.length, cursor.getColumnCount());
|
||||
assertEquals(
|
||||
"column names",
|
||||
new HashSet<>(Arrays.asList(expectedColumns)),
|
||||
new HashSet<>(Arrays.asList(cursor.getColumnNames()))
|
||||
);
|
||||
|
||||
cursor.moveToNext();
|
||||
|
||||
final int actualId = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
|
||||
final String actualName = cursor.getString(cursor.getColumnIndexOrThrow("store"));
|
||||
final String actualNote = cursor.getString(cursor.getColumnIndexOrThrow("note"));
|
||||
final long actualValidFrom = cursor.getLong(cursor.getColumnIndexOrThrow("validfrom"));
|
||||
final long actualExpiry = cursor.getLong(cursor.getColumnIndexOrThrow("expiry"));
|
||||
final BigDecimal actualBalance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow("balance")));
|
||||
final String actualBalanceType = cursor.getString(cursor.getColumnIndexOrThrow("balancetype"));
|
||||
final String actualCardId = cursor.getString(cursor.getColumnIndexOrThrow("cardid"));
|
||||
final String actualBarcodeId = cursor.getString(cursor.getColumnIndexOrThrow("barcodeid"));
|
||||
final String actualBarcodeType = cursor.getString(cursor.getColumnIndexOrThrow("barcodetype"));
|
||||
final int actualHeaderColor = cursor.getInt(cursor.getColumnIndexOrThrow("headercolor"));
|
||||
final int actualStarred = cursor.getInt(cursor.getColumnIndexOrThrow("starstatus"));
|
||||
final long actualLastUsed = cursor.getLong(cursor.getColumnIndexOrThrow("lastused"));
|
||||
final int actualArchiveStatus = cursor.getInt(cursor.getColumnIndexOrThrow("archive"));
|
||||
|
||||
assertEquals("Id", 1, actualId);
|
||||
assertEquals("Name", store, actualName);
|
||||
assertEquals("Note", note, actualNote);
|
||||
assertEquals("ValidFrom", validFrom.getTime(), actualValidFrom);
|
||||
assertEquals("Expiry", expiry.getTime(), actualExpiry);
|
||||
assertEquals("Balance", balance, actualBalance);
|
||||
assertEquals("BalanceTypeColumn", balanceType.toString(), actualBalanceType);
|
||||
assertEquals("CardId", cardId, actualCardId);
|
||||
assertEquals("BarcodeId", barcodeId, actualBarcodeId);
|
||||
assertEquals("BarcodeType", barcodeType.format().name(), actualBarcodeType);
|
||||
assertEquals("HeaderColorColumn", headerColor, actualHeaderColor);
|
||||
assertEquals("Starred", starStatus, actualStarred);
|
||||
assertEquals("LastUsed", lastUsed, actualLastUsed);
|
||||
assertEquals("ArchiveStatus", archiveStatus, actualArchiveStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCardsProjection() {
|
||||
final Uri cardsUri = getUri("cards");
|
||||
|
||||
try (Cursor cursor = mResolver.query(cardsUri, null, null, null)) {
|
||||
assertEquals(cursor.getCount(), 0);
|
||||
}
|
||||
|
||||
TestHelpers.addLoyaltyCards(mDatabase, 1);
|
||||
|
||||
// Query with projection of columns, including internal column names, which should be filtered out
|
||||
try (Cursor cursor = mResolver.query(cardsUri, new String[] {"_id", "store", "zoomlevel"}, null, null)) {
|
||||
assertEquals("number of cards", 1, cursor.getCount());
|
||||
|
||||
assertEquals("number of columns", 2, cursor.getColumnCount());
|
||||
assertArrayEquals("column names", new String[]{"_id", "store"}, cursor.getColumnNames());
|
||||
|
||||
cursor.moveToNext();
|
||||
|
||||
final int actualId = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
|
||||
final String actualName = cursor.getString(cursor.getColumnIndexOrThrow("store"));
|
||||
|
||||
assertEquals("id", 1, actualId);
|
||||
assertEquals("store", "store, \" 1", actualName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroups() {
|
||||
final Uri groupsUri = getUri("groups");
|
||||
|
||||
try (Cursor cursor = mResolver.query(groupsUri, null, null, null)) {
|
||||
assertEquals("start without groups", 0, cursor.getCount());
|
||||
}
|
||||
|
||||
TestHelpers.addGroups(mDatabase, 4);
|
||||
|
||||
try (Cursor cursor = mResolver.query(groupsUri, null, null, null)) {
|
||||
assertEquals("number of groups", 4, cursor.getCount());
|
||||
assertEquals("number of columns", 2, cursor.getColumnCount());
|
||||
assertArrayEquals("column names", new String[]{"_id", "orderId"}, cursor.getColumnNames());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
cursor.moveToNext();
|
||||
assertEquals(
|
||||
String.format("groups[%d]._id", i),
|
||||
String.format("group, \"%4d", 4 - i),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow("_id"))
|
||||
);
|
||||
assertEquals(
|
||||
String.format("groups[%d].orderId", i),
|
||||
String.valueOf(i),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow("orderId"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCardGroups() {
|
||||
final Uri cardGroupsUri = getUri("card_groups");
|
||||
|
||||
try (Cursor cursor = mResolver.query(cardGroupsUri, null, null, null)) {
|
||||
assertEquals(cursor.getCount(), 0);
|
||||
}
|
||||
|
||||
TestHelpers.addLoyaltyCards(mDatabase, 5);
|
||||
TestHelpers.addGroups(mDatabase, 4);
|
||||
|
||||
final List<Group> groupsForOne = new ArrayList<>();
|
||||
groupsForOne.add(DBHelper.getGroup(mDatabase, "group, \" 1"));
|
||||
|
||||
final List<Group> groupsForTwo = new ArrayList<>();
|
||||
groupsForTwo.add(DBHelper.getGroup(mDatabase, "group, \" 1"));
|
||||
groupsForTwo.add(DBHelper.getGroup(mDatabase, "group, \" 2"));
|
||||
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, 1, groupsForOne);
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, 2, groupsForTwo);
|
||||
|
||||
final Map<String, List<String>> expectedGroups = new HashMap<>() {{
|
||||
put("group, \" 1", Arrays.asList("1", "2"));
|
||||
put("group, \" 2", Collections.singletonList("2"));
|
||||
}};
|
||||
|
||||
try (Cursor cursor = mResolver.query(cardGroupsUri, null, null, null)) {
|
||||
assertEquals("number of card groups", 3, cursor.getCount());
|
||||
assertEquals("number of columns", 2, cursor.getColumnCount());
|
||||
assertArrayEquals("column names", new String[]{"cardId", "groupId"}, cursor.getColumnNames());
|
||||
|
||||
final Map<String, List<String>> groups = new HashMap<>();
|
||||
while (cursor.moveToNext()) {
|
||||
final String cardId = cursor.getString(cursor.getColumnIndexOrThrow("cardId"));
|
||||
final String groupId = cursor.getString(cursor.getColumnIndexOrThrow("groupId"));
|
||||
groups.computeIfAbsent(groupId, k -> new ArrayList<>()).add(cardId);
|
||||
}
|
||||
assertEquals("expected groups with cards", expectedGroups, groups);
|
||||
}
|
||||
}
|
||||
|
||||
private Uri getUri(final String endpoint) {
|
||||
return Uri.parse(String.format(Locale.ROOT, "content://%s/%s", CardsContentProvider.AUTHORITY, endpoint));
|
||||
}
|
||||
}
|
||||
Binary file not shown.
78
docs/CONTENT_PROVIDER.md
Normal file
78
docs/CONTENT_PROVIDER.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Content Provider
|
||||
|
||||
Catima implements a [content provider](https://developer.android.com/guide/topics/providers/content-providers) to allow for external apps to interact with the cards database. Right now, this only provides read-only functionality.
|
||||
|
||||
Since runtime permissions are only available since Android 6.0 (API level 23), the content provider is disabled for older android versions in order to prevent unwanted access to the data.
|
||||
|
||||
## Package Names
|
||||
|
||||
There are 3 release channels, with 2 possible package names:
|
||||
|
||||
| Release Channel | Package Name |
|
||||
|-----------------|-----------------------------|
|
||||
| Google Play | me.hackerchick.catima |
|
||||
| F-Droid | me.hackerchick.catima |
|
||||
| Debug Build | me.hackerchick.catima.debug |
|
||||
|
||||
The package names are required for the authority and permissions, as described below.
|
||||
|
||||
## Authority
|
||||
|
||||
The authority for this content provider: `<package_name>.contentprovider.cards`
|
||||
|
||||
## Permissions
|
||||
|
||||
The content provider requires the following permissions:
|
||||
|
||||
- `<package_name>.READ_CARDS` - in order to access any of the URIs.
|
||||
|
||||
## URIs
|
||||
|
||||
### /version
|
||||
|
||||
Returns a single record with the current API version for the content provider.
|
||||
|
||||
A major version change implies breaking changes (eg. columns being renamed or removed).
|
||||
|
||||
| Column | Type | Description | Value |
|
||||
|---------|-------|-------------------|-------|
|
||||
| `major` | `int` | The major version | `1` |
|
||||
| `minor` | `int` | The minor version | `0` |
|
||||
|
||||
### /cards
|
||||
|
||||
| Column | Type | Description |
|
||||
|---------------|----------|----------------------------|
|
||||
| `_id` | `int` | Unique card ID |
|
||||
| `store` | `String` | Card name |
|
||||
| `validfrom` | `long` | Timestamp from which the card is valid (unix epoch millis). |
|
||||
| `expiry` | `long` | Expiration timestamp (unix epoch millis). |
|
||||
| `balance` | `String` | Current balance, as a string-formatted big decimal. |
|
||||
| `balancetype` | `String` | Balance currency code, ISO 4217. |
|
||||
| `note` | `String` | A note. |
|
||||
| `headercolor` | `int` | Header color, in RGBA. |
|
||||
| `cardid` | `String` | Card ID. |
|
||||
| `barcodeid` | `String` | Barcode value. If empty, it's the same as the card ID. |
|
||||
| `barcodetype` | `String` | The barcode type name, matching [com.google.zxing.BarcodeFormat](https://zxing.github.io/zxing/apidocs/com/google/zxing/BarcodeFormat.html). |
|
||||
| `starstatus` | `int` | 1 if starred, 0 if not |
|
||||
| `lastused` | `long` | Timestamp of last card usage (unix epoch millis). |
|
||||
| `archive` | `int` | 1 if archived, 0 if not |
|
||||
|
||||
### /groups
|
||||
|
||||
| Column | Type | Description |
|
||||
|-----------|----------|----------------------------|
|
||||
| `_id` | `String` | Group name (unique) |
|
||||
| `orderId` | `int` | Group order, in the UI |
|
||||
|
||||
### /card_groups
|
||||
|
||||
Returns the mapping between cards and groups, by ID.
|
||||
|
||||
- A card can be in 0 or more groups.
|
||||
- A group can contain 0 or more cards.
|
||||
|
||||
| Column | Type | Description |
|
||||
|-----------|----------|--------------|
|
||||
| `cardId` | `String` | Card ID |
|
||||
| `groupId` | `String` | Group ID |
|
||||
@@ -58,7 +58,7 @@ For FidMe you need to select the barcode type for each entry afterwards.
|
||||
|
||||
# Building
|
||||
|
||||
Building can either be done through Android Studio (not reproducible!) or the build.sh script in this repository (reproducibly with Java 11, same way F-Droid builds it). This script can also sign the build.
|
||||
Building can either be done through Android Studio (not reproducible!) or the build.sh script in this repository (reproducibly with OpenJDK 17, same way F-Droid builds it). This script can also sign the build.
|
||||
|
||||
Build without signing:
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# When releasing, do the following:
|
||||
1. Press "Commit" and "Push" on Weblate to ensure all translations are up to date
|
||||
2. Merge Weblate pull request
|
||||
3. Update `CHANGELOG.md` with the new version name
|
||||
3. Update `CHANGELOG.md` with the new version name and the release date
|
||||
4. Update `app/build.gradle` with the new `versionCode` and `versionName`
|
||||
5. Build a new .apk: `KEYSTORE=/path/to/keystore KEYSTORE_ALIAS=catima ./build.sh`
|
||||
6. Upload the APK to Google Play Open Testing
|
||||
|
||||
5
docs/STOCARD.md
Normal file
5
docs/STOCARD.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Stocard Importer
|
||||
|
||||
The `app/src/main/res/raw/stocard_stores.csv` CSV file used by the Stocard importer was created using the `.scripts/dump_stocard_stores.py` script.
|
||||
|
||||
Only used for data portability reasons (ensuring importing works). Do NOT copy this anywhere else or use it for any purpose other than ensuring we can import a GDPR-provided export. We want to make sure this stays under fair use.
|
||||
2
fastlane/metadata/android/ar/changelogs/100.txt
Normal file
2
fastlane/metadata/android/ar/changelogs/100.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
- إخفاء البحث وتوسيع الأيقونات حتى تبقى بطاقة واحدة على الأقل
|
||||
- تغييرات مختلفة على الثيمات
|
||||
1
fastlane/metadata/android/ast/title.txt
Normal file
1
fastlane/metadata/android/ast/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
Catima - Cartera de tarxetes de fidelización
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user