mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2025-12-24 15:47:53 -05:00
Compare commits
2 Commits
create-pul
...
fix/1744
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aad062e3d | ||
|
|
dd09d0e130 |
66
.github/workflows/android.yml
vendored
66
.github/workflows/android.yml
vendored
@@ -28,47 +28,27 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
flavor: [Foss, Gplay]
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- 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/actions/wrapper-validation@v4
|
||||
- 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 assemble${{ matrix.flavor }}Release
|
||||
- name: Check lint
|
||||
run: ./gradlew lint${{ matrix.flavor }}Release
|
||||
- name: Run unit tests
|
||||
run: timeout 5m ./gradlew test${{ matrix.flavor }}ReleaseUnitTest || { ./gradlew --stop && timeout 5m ./gradlew test${{ matrix.flavor }}ReleaseUnitTest; }
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
- name: Run instrumented tests (API 21)
|
||||
uses: ReactiveCircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 21
|
||||
arch: x86_64
|
||||
script: ./gradlew connected${{ matrix.flavor }}DebugAndroidTest
|
||||
- name: Run instrumented tests (API 34)
|
||||
uses: ReactiveCircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 34
|
||||
arch: x86_64
|
||||
script: ./gradlew connected${{ matrix.flavor }}DebugAndroidTest
|
||||
- name: SpotBugs
|
||||
run: ./gradlew spotbugs${{ matrix.flavor }}Release
|
||||
- name: Archive test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4.6.1
|
||||
with:
|
||||
name: test-results-flavor${{ matrix.flavor }}
|
||||
path: app/build/reports
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- 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@v2
|
||||
- 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: timeout 5m ./gradlew testReleaseUnitTest || { ./gradlew --stop && timeout 5m ./gradlew testReleaseUnitTest; }
|
||||
- name: SpotBugs
|
||||
run: ./gradlew spotbugsRelease
|
||||
- name: Archive test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: test-results
|
||||
path: app/build/reports
|
||||
|
||||
6
.github/workflows/changelog-to-fastlane.yml
vendored
6
.github/workflows/changelog-to-fastlane.yml
vendored
@@ -27,15 +27,15 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
id: checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5.4.0
|
||||
uses: actions/setup-python@v5.0.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Run converter script
|
||||
run: python .scripts/changelog_to_fastlane.py
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.7
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
with:
|
||||
title: "Update Fastlane changelogs"
|
||||
commit-message: "Update Fastlane changelogs"
|
||||
|
||||
4
.github/workflows/contributors-to-file.yml
vendored
4
.github/workflows/contributors-to-file.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
id: checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Update contributors
|
||||
id: update_contributors
|
||||
uses: TheLastProject/contributors-to-file-action@v3.2.0
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
file_in_repo: app/src/main/res/raw/contributors.txt
|
||||
min_commit_count: 5
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.7
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
with:
|
||||
title: "Update contributors"
|
||||
commit-message: "Update contributors"
|
||||
|
||||
@@ -6,7 +6,6 @@ on:
|
||||
- main
|
||||
paths:
|
||||
- 'fastlane/**/title.txt'
|
||||
- '.scripts/generate_feature_graphic/**'
|
||||
permissions:
|
||||
actions: none
|
||||
checks: none
|
||||
@@ -25,11 +24,11 @@ jobs:
|
||||
generate-feature-graphic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- name: Install requirements
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install imagemagick mat2 optipng
|
||||
sudo apt-get install optipng mat2
|
||||
# Install 200 weight versions of relevant Noto (to use for languages not supported by Lexend Deca)
|
||||
sudo apt-get install fonts-noto-extra fonts-noto-cjk-extra
|
||||
# Custom fonts
|
||||
@@ -39,7 +38,7 @@ jobs:
|
||||
- name: Generate featureGraphic.png for each language
|
||||
run: .scripts/generate_feature_graphic/generate_feature_graphic.sh
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.7
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
with:
|
||||
title: "Update feature graphic"
|
||||
commit-message: "Update feature graphic"
|
||||
|
||||
8
.github/workflows/gradle-update.yml
vendored
8
.github/workflows/gradle-update.yml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
gradle-update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: obfusk/gradle-update-action@v3.0.0
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- uses: obfusk/gradle-update-action@v2.0.0
|
||||
id: gradle-update
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.7
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
with:
|
||||
title: "Update Gradle to ${{ steps.gradle-update.outputs.version }}"
|
||||
commit-message: "Update Gradle to ${{ steps.gradle-update.outputs.version }}"
|
||||
|
||||
4
.github/workflows/update-locales.yml
vendored
4
.github/workflows/update-locales.yml
vendored
@@ -25,13 +25,13 @@ jobs:
|
||||
update-locales:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- uses: actions/checkout@v4.1.1
|
||||
- name: Add new locales
|
||||
run: .scripts/new-locales.py
|
||||
- name: Update locales
|
||||
run: .scripts/locales.py
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7.0.7
|
||||
uses: peter-evans/create-pull-request@v6.0.0
|
||||
with:
|
||||
title: "Update locales"
|
||||
commit-message: "Update locales"
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -25,6 +25,3 @@
|
||||
/.bundle/
|
||||
/vendor/bundle
|
||||
/lib/bundler/man/
|
||||
|
||||
# Catima-specific
|
||||
SHA256SUMS
|
||||
|
||||
@@ -4,11 +4,6 @@ set -euo pipefail
|
||||
script_location="$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
for lang in "$script_location/../../fastlane/metadata/android/"*; do
|
||||
# Skip languages without title.txt
|
||||
if [ ! -f "$lang/title.txt" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
pushd "$lang"
|
||||
# Place temporary copy for editing if needed
|
||||
cp "$script_location/featureGraphic.svg" featureGraphic.svg
|
||||
@@ -18,18 +13,14 @@ for lang in "$script_location/../../fastlane/metadata/android/"*; do
|
||||
elif grep -q – title.txt; then
|
||||
# No result, try splitting title.txt on – (en dash)
|
||||
IFS='–' read -r appname subtext < title.txt
|
||||
elif grep -q - title.txt; then
|
||||
else
|
||||
# No result, try splitting on - (dash)
|
||||
IFS='-' read -r appname subtext < title.txt
|
||||
else
|
||||
# No result, use the full title as app name and default subtext
|
||||
appname=$(< title.txt)
|
||||
subtext="Loyalty Card Wallet"
|
||||
fi
|
||||
export appname=${appname%% }
|
||||
export subtext=${subtext## }
|
||||
# If the appname isn't Catima or there is subtext, change the .svg accordingly
|
||||
if [ "$appname" != "Catima" ] || [ -n "$subtext" ]; then
|
||||
# If there is subtext, change the .svg accordingly
|
||||
if [ -n "$subtext" ]; then
|
||||
perl -pi -e 's/Catima/$ENV{appname}/' featureGraphic.svg
|
||||
perl -pi -e 's/Loyalty Card Wallet/$ENV{subtext}/' featureGraphic.svg
|
||||
# Set correct font or font size for language if needed
|
||||
@@ -37,12 +28,12 @@ for lang in "$script_location/../../fastlane/metadata/android/"*; do
|
||||
# We specifically need the Serif version because of the 200 weight
|
||||
case "$(basename "$lang")" in
|
||||
bg|el-GR|ru-RU|uk) sed -i "s/Lexend Deca/Noto Serif/" featureGraphic.svg ;;
|
||||
hi-IN) sed -i -e "s/Yesteryear/Noto Sans Devanagari/" -e "s/Lexend Deca/Noto Serif Devanagari/" featureGraphic.svg ;;
|
||||
hi-IN) sed -i -e "s/Yesteryear/Noto Serif Devanagari/" -e "s/Lexend Deca/Noto Serif Devanagari/" featureGraphic.svg ;;
|
||||
ja-JP) sed -i "s/Lexend Deca/Noto Serif CJK JP/" featureGraphic.svg ;;
|
||||
kn-IN) sed -i -e 's/font-size="150"/font-size="100"/' -e "s/Yesteryear/Noto Serif Kannada/" featureGraphic.svg ;;
|
||||
ko) sed -i "s/Lexend Deca/Noto Serif CJK KR/" featureGraphic.svg ;;
|
||||
kn-IN) sed -i -e 's/font-size="150"/font-size="100"/' -e 's/y="285.511"/y="235.511"/' featureGraphic.svg ;;
|
||||
zh-CN) sed -i "s/Lexend Deca/Noto Serif CJK SC/" featureGraphic.svg ;;
|
||||
zh-TW) sed -i -e "s/Yesteryear/Noto Sans CJK TC/" -e "s/Lexend Deca/Noto Serif CJK TC/" featureGraphic.svg ;;
|
||||
zh-TW) sed -i "s/Lexend Deca/Noto Serif CJK TC/" featureGraphic.svg ;;
|
||||
*) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -24,7 +24,7 @@ sed = [
|
||||
]
|
||||
subprocess.run(sed, check=True)
|
||||
|
||||
with open("app/src/main/res/xml/locales_config.xml", "w", encoding="utf-8") as fh:
|
||||
with open("app/src/main/res/xml/locales_config.xml", "w") as fh:
|
||||
fh.write('<?xml version="1.0" encoding="utf-8"?>\n')
|
||||
fh.write('<locale-config xmlns:android="http://schemas.android.com/apk/res/android">\n')
|
||||
fh.write(' <locale android:name="en-US" />\n')
|
||||
|
||||
@@ -19,27 +19,15 @@ REPLACE_CODES = {
|
||||
STATS_URL = "https://hosted.weblate.org/api/components/catima/catima/statistics/"
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_weblate_langs() -> List[Tuple[str, int]]:
|
||||
url = STATS_URL
|
||||
r = requests.get(STATS_URL, timeout=5)
|
||||
r.raise_for_status()
|
||||
results = []
|
||||
for _ in range(16): # avoid endless loops just in case
|
||||
r = requests.get(url, timeout=5)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
for lang in data["results"]:
|
||||
if lang["code"] != "en":
|
||||
code = REPLACE_CODES.get(lang["code"], lang["code"]).replace("_", "-r")
|
||||
results.append((code, round(lang["translated_percent"])))
|
||||
url = data["next"]
|
||||
if not url:
|
||||
return sorted(results)
|
||||
if not url.split("?")[0] == STATS_URL:
|
||||
raise Error(f"Unexpected next URL: {url}")
|
||||
raise Error("Too many pages")
|
||||
for lang in r.json()["results"]:
|
||||
if lang["code"] != "en":
|
||||
code = REPLACE_CODES.get(lang["code"], lang["code"]).replace("_", "-r")
|
||||
results.append((code, round(lang["translated_percent"])))
|
||||
return sorted(results)
|
||||
|
||||
|
||||
def get_dir_langs() -> List[str]:
|
||||
@@ -54,7 +42,7 @@ def get_dir_langs() -> List[str]:
|
||||
def get_xml_langs() -> List[Tuple[str, bool]]:
|
||||
results = []
|
||||
in_section = False
|
||||
with open("app/src/main/res/values/settings.xml", encoding="utf-8") as fh:
|
||||
with open("app/src/main/res/values/settings.xml") as fh:
|
||||
for line in fh:
|
||||
if not in_section and 'name="locale_values"' in line:
|
||||
in_section = True
|
||||
@@ -71,7 +59,7 @@ def get_xml_langs() -> List[Tuple[str, bool]]:
|
||||
def update_xml_langs(langs: List[Tuple[str, bool]]) -> None:
|
||||
lines: List[str] = []
|
||||
in_section = False
|
||||
with open("app/src/main/res/values/settings.xml", encoding="utf-8") as fh:
|
||||
with open("app/src/main/res/values/settings.xml") as fh:
|
||||
for line in fh:
|
||||
if not in_section and 'name="locale_values"' in line:
|
||||
in_section = True
|
||||
@@ -82,7 +70,7 @@ def update_xml_langs(langs: List[Tuple[str, bool]]) -> None:
|
||||
else:
|
||||
continue
|
||||
lines.append(line)
|
||||
with open("app/src/main/res/values/settings.xml", "w", encoding="utf-8") as fh:
|
||||
with open("app/src/main/res/values/settings.xml", "w") as fh:
|
||||
for line in lines:
|
||||
fh.write(line)
|
||||
|
||||
|
||||
73
CHANGELOG.md
73
CHANGELOG.md
@@ -1,81 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## v2.34.4 - 146 (2025-01-17)
|
||||
|
||||
- Ability to sort cards by start of validity
|
||||
- Temporarily revert to targeting Android 14 to fix some UI issues
|
||||
|
||||
## v2.34.3 - 145 (2025-01-15)
|
||||
|
||||
- Target Android 15
|
||||
- Fix keyboard covering save button in edit screen
|
||||
- Fix some pkpass files not being detected as pkpass (application/vnd-com.apple.pkpass mime type support)
|
||||
|
||||
## v2.34.2 - 144 (2024-12-26)
|
||||
|
||||
- Improve archive/starred icon display
|
||||
|
||||
## v2.34.1 - 143 (2024-12-12)
|
||||
|
||||
- Fix crash when opening invalid pkpass files
|
||||
|
||||
## v2.34.0 - 142 (2024-12-10)
|
||||
|
||||
- Add Passbook (.pkpass) support
|
||||
- Fix import of transparent PDF files
|
||||
- Improve display of transparent thumbnails
|
||||
|
||||
## v2.33.0 - 141 (2024-11-19)
|
||||
|
||||
- Change default column on wide screens to 4
|
||||
- Allow overriding column counts for portrait and landscape in settings
|
||||
- Keep main screen search filter when rotating screen or opening a card
|
||||
- Limit max length of note display on main screen
|
||||
|
||||
## v2.32.1 - 140 (2024-10-29)
|
||||
|
||||
- Fix text wrapping on add dialog
|
||||
|
||||
## v2.32.0 - 139 (2024-10-28)
|
||||
|
||||
- Option to navigate cards using the volume buttons
|
||||
- Fix Stocard import
|
||||
- Fix "Import cancelled" message appearing after successful import
|
||||
|
||||
## v2.31.1 - 138 (2024-08-24)
|
||||
|
||||
- Fix back gesture on main screen dismissing keyboard and search on Android 13+
|
||||
|
||||
## v2.31.0 - 137 (2024-07-26)
|
||||
|
||||
- Allow long store names in preview to split over multiple lines
|
||||
- Option to use front of back image in thumbnail menu
|
||||
- Minor import/export fixes
|
||||
- Minor UI fixes
|
||||
|
||||
## v2.30.0 - 136 (2024-06-18)
|
||||
|
||||
- Support for creating a card when sharing plain text
|
||||
- Display image type instead of barcode below images
|
||||
- Fix possible crash when trying to import a backup from the Nextcloud app
|
||||
- Improved support for devices without camera
|
||||
|
||||
## v2.29.1 - 135 (2024-05-19)
|
||||
|
||||
- Various fixes and improvements to balance handling
|
||||
|
||||
## v2.29.0 - 134 (2024-04-19)
|
||||
|
||||
- Support for scanning PDF files for barcodes
|
||||
- Support for image files with multiple barcodes
|
||||
- Minor UI fixes
|
||||
|
||||
## v2.28.0 - 133 (2024-03-08)
|
||||
## Unreleased - 133
|
||||
|
||||
- Target Android 14
|
||||
- Open card icon in gallery on touch
|
||||
- Improve design of Photos tab in edit view
|
||||
- Update spending screen to also support receiving
|
||||
|
||||
## v2.27.0 - 132 (2024-01-30)
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
catima.g9ex3@hackerchick.me.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
105
Gemfile.lock
105
Gemfile.lock
@@ -1,32 +1,29 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
artifactory (3.0.17)
|
||||
addressable (2.8.6)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.1020.0)
|
||||
aws-sdk-core (3.214.0)
|
||||
aws-partitions (1.884.0)
|
||||
aws-sdk-core (3.191.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.8)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.96.0)
|
||||
aws-sdk-core (~> 3, >= 3.210.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.176.0)
|
||||
aws-sdk-core (~> 3, >= 3.210.0)
|
||||
aws-sdk-kms (1.77.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.143.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.10.1)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (1.8.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
base64 (0.2.0)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
@@ -38,8 +35,8 @@ GEM
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.112.0)
|
||||
faraday (1.10.4)
|
||||
excon (0.109.0)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
@@ -60,22 +57,22 @@ GEM
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.3.1)
|
||||
fastlane (2.226.0)
|
||||
fastimage (2.3.0)
|
||||
fastlane (2.219.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored (~> 1.2)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
@@ -84,7 +81,6 @@ GEM
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
fastlane-sirp (>= 1.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
@@ -97,10 +93,10 @@ GEM
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (>= 0.1.1, < 1.0.0)
|
||||
optparse (>= 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.5)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (~> 3)
|
||||
@@ -108,10 +104,8 @@ GEM
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.4.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||
fastlane-sirp (1.0.0)
|
||||
sysrandom (~> 1.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.54.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
@@ -129,12 +123,12 @@ GEM
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.31.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.7.1)
|
||||
google-cloud-core (1.6.1)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.4.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
google-cloud-storage (1.47.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
@@ -150,36 +144,34 @@ GEM
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.8)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.9.0)
|
||||
jwt (2.9.3)
|
||||
base64
|
||||
mini_magick (4.13.2)
|
||||
json (2.7.1)
|
||||
jwt (2.7.1)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.5)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.1)
|
||||
nanaimo (0.4.0)
|
||||
multipart-post (2.3.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
nkf (0.2.0)
|
||||
optparse (0.6.0)
|
||||
optparse (0.4.0)
|
||||
os (1.1.4)
|
||||
plist (3.7.1)
|
||||
public_suffix (6.0.1)
|
||||
rake (13.2.1)
|
||||
public_suffix (5.0.4)
|
||||
rake (13.1.0)
|
||||
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.3.9)
|
||||
rouge (3.28.0)
|
||||
rexml (3.2.6)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.5)
|
||||
signet (0.19.0)
|
||||
security (0.1.3)
|
||||
signet (0.18.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
@@ -187,7 +179,6 @@ GEM
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
sysrandom (1.0.5)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
@@ -197,17 +188,17 @@ GEM
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (2.6.0)
|
||||
unicode-display_width (2.5.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.27.0)
|
||||
xcodeproj (1.24.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.4.0)
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
xcpretty (0.4.0)
|
||||
rouge (~> 3.28.0)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
@@ -218,4 +209,4 @@ DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.22
|
||||
2.3.26
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.github.spotbugs.snom.SpotBugsTask
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("com.github.spotbugs")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
spotbugs {
|
||||
@@ -16,24 +15,19 @@ spotbugs {
|
||||
|
||||
android {
|
||||
namespace = "protect.card_locker"
|
||||
compileSdk = 35
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "me.hackerchick.catima"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 146
|
||||
versionName = "2.34.4"
|
||||
versionCode = 132
|
||||
versionName = "2.27.0"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled = true
|
||||
|
||||
resourceConfigurations += listOf("ar", "bg", "bn", "bn-rIN", "bs", "cs", "da", "de", "el-rGR", "en", "eo", "es", "es-rAR", "et", "fi", "fr", "gl", "he-rIL", "hi", "hr", "hu", "in-rID", "is", "it", "ja", "ko", "lt", "lv", "nb-rNO", "nl", "oc", "pl", "pt", "pt-rBR", "pt-rPT", "ro-rRO", "ru", "sk", "sl", "sr", "sv", "ta", "tr", "uk", "vi", "zh-rCN", "zh-rTW")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
buildConfigField("boolean", "showDonate", "true")
|
||||
buildConfigField("boolean", "showRateOnGooglePlay", "false")
|
||||
resourceConfigurations += listOf("ar", "bg", "bn", "bn-rIN", "bs", "cs", "da", "de", "el-rGR", "en", "eo", "es", "es-rAR", "fi", "fr", "he-rIL", "hi", "hr", "hu", "in-rID", "is", "it", "ja", "ko", "lt", "lv", "nb-rNO", "nl", "oc", "pl", "pt-rPT", "ro-rRO", "ru", "sk", "sl", "sv", "tr", "uk", "vi", "zh-rCN", "zh-rTW")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -54,21 +48,6 @@ android {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
flavorDimensions.add("type")
|
||||
productFlavors {
|
||||
create("foss") {
|
||||
dimension = "type"
|
||||
isDefault = true
|
||||
}
|
||||
create("gplay") {
|
||||
dimension = "type"
|
||||
|
||||
// Google doesn't allow donation links
|
||||
buildConfigField("boolean", "showDonate", "false")
|
||||
buildConfigField("boolean", "showRateOnGooglePlay", "true")
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
language {
|
||||
enableSplit = false
|
||||
@@ -81,8 +60,8 @@ android {
|
||||
// Flag to enable support for the new language APIs
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -103,26 +82,25 @@ android {
|
||||
lint {
|
||||
lintConfig = file("lint.xml")
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// AndroidX
|
||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||
implementation("androidx.core:core-ktx:1.15.0")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.exifinterface:exifinterface:1.4.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.exifinterface:exifinterface:1.3.7")
|
||||
implementation("androidx.palette:palette:1.0.0")
|
||||
implementation("androidx.preference:preference:1.2.1")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
implementation("com.github.yalantis:ucrop:2.2.8")
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||
|
||||
// Splash Screen
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
// Third-party
|
||||
implementation("com.journeyapps:zxing-android-embedded:4.3.0@aar")
|
||||
implementation("com.github.yalantis:ucrop:2.2.10")
|
||||
implementation("com.google.zxing:core:3.5.3")
|
||||
implementation("org.apache.commons:commons-csv:1.9.0")
|
||||
implementation("com.jaredrummler:colorpicker:1.1.0")
|
||||
@@ -132,18 +110,9 @@ dependencies {
|
||||
implementation("io.wcm.tooling.spotbugs:io.wcm.tooling.spotbugs.annotations:1.0.0")
|
||||
|
||||
// Testing
|
||||
val androidXTestVersion = "1.6.1"
|
||||
val junitVersion = "4.13.2"
|
||||
testImplementation("androidx.test:core:$androidXTestVersion")
|
||||
testImplementation("junit:junit:$junitVersion")
|
||||
testImplementation("org.robolectric:robolectric:4.14.1")
|
||||
|
||||
androidTestImplementation("androidx.test:core:$androidXTestVersion")
|
||||
androidTestImplementation("junit:junit:$junitVersion")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
||||
androidTestImplementation("androidx.test:runner:$androidXTestVersion")
|
||||
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
||||
testImplementation("androidx.test:core:1.5.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.robolectric:robolectric:4.11.1")
|
||||
}
|
||||
|
||||
tasks.withType<SpotBugsTask>().configureEach {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.typeText;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withChild;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MainActivitySearchViewTest {
|
||||
|
||||
@Test
|
||||
public void whenSearchViewIsExpandedAndBackIsPressedThenMenuItemShouldNotBeCollapsed() {
|
||||
String query = "random arbitrary text";
|
||||
try (ActivityScenario<MainActivity> mainActivityScenario = ActivityScenario.launch(MainActivity.class)) {
|
||||
mainActivityScenario.onActivity(this::makeSearchMenuItemVisible);
|
||||
onView(withId(R.id.action_search)).perform(click());
|
||||
onView(withId(androidx.appcompat.R.id.search_src_text)).perform(typeText(query));
|
||||
|
||||
pressBack();
|
||||
|
||||
onView(withId(androidx.appcompat.R.id.search_src_text)).check(matches(withText(query)));
|
||||
mainActivityScenario.onActivity(activity -> assertEquals(query, activity.mFilter));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSearchViewIsExpandedThenItShouldOnlyBeCollapsedWhenBackIsPressedTwice() {
|
||||
try (ActivityScenario<MainActivity> mainActivityScenario = ActivityScenario.launch(MainActivity.class)) {
|
||||
mainActivityScenario.onActivity(this::makeSearchMenuItemVisible);
|
||||
onView(withId(R.id.action_search)).perform(click());
|
||||
|
||||
pressBack();
|
||||
|
||||
onView(withId(androidx.appcompat.R.id.search_src_text)).check(matches(isDisplayed()));
|
||||
|
||||
pressBack();
|
||||
|
||||
onView(withId(android.R.id.content)).check(matches(is(not(withChild(withId(androidx.appcompat.R.id.search_src_text))))));
|
||||
}
|
||||
}
|
||||
|
||||
private void makeSearchMenuItemVisible(MainActivity activity) {
|
||||
Toolbar toolbar = activity.findViewById(R.id.toolbar);
|
||||
toolbar.getMenu().findItem(R.id.action_search).setVisible(true);
|
||||
}
|
||||
|
||||
private void pressBack() {
|
||||
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">تصحيح Catima</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Debugar Catima</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Αποσφαλμάτωση Catima</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima Debug</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima Debug</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima-vianmääritys</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Depuración de Catima</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima atkļūdošana</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Depuração do Catima</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima Debug</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -1,4 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Catima 除錯版</string>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
android:required="true" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
@@ -39,29 +39,11 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="content"/>
|
||||
<data android:host="*"/>
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="application/pdf" />
|
||||
<data android:mimeType="application/vnd.apple.pkpass" />
|
||||
<data android:mimeType="application/vnd-com.apple.pkpass" />
|
||||
<data android:mimeType="application/vnd.espass-espass" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/plain" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="application/pdf" />
|
||||
<data android:mimeType="application/vnd.apple.pkpass" />
|
||||
<data android:mimeType="application/vnd-com.apple.pkpass" />
|
||||
<data android:mimeType="application/vnd.espass-espass" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
@@ -79,12 +61,13 @@
|
||||
<activity
|
||||
android:name=".LoyaltyCardViewActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name=".LoyaltyCardEditActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
<intent-filter
|
||||
android:autoVerify="true"
|
||||
android:label="@string/app_name">
|
||||
@@ -122,7 +105,8 @@
|
||||
<activity
|
||||
android:name=".BarcodeSelectorActivity"
|
||||
android:label="@string/selectBarcodeTitle"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
<activity
|
||||
android:name=".preferences.SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
@@ -197,8 +181,7 @@
|
||||
android:resource="@xml/file_provider_paths" />
|
||||
</provider>
|
||||
<service android:name=".CardsOnPowerScreenService" android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_CONTROLS" android:exported="true"
|
||||
tools:targetApi="r">
|
||||
android:permission="android.permission.BIND_CONTROLS" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.controls.ControlsProviderService" />
|
||||
</intent-filter>
|
||||
|
||||
147
app/src/main/java/protect/card_locker/AboutActivity.java
Normal file
147
app/src/main/java/protect/card_locker/AboutActivity.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Spanned;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import protect.card_locker.databinding.AboutActivityBinding;
|
||||
|
||||
public class AboutActivity extends CatimaAppCompatActivity {
|
||||
|
||||
private static final String TAG = "Catima";
|
||||
|
||||
private AboutActivityBinding binding;
|
||||
private AboutContent content;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = AboutActivityBinding.inflate(getLayoutInflater());
|
||||
content = new AboutContent(this);
|
||||
setTitle(content.getPageTitle());
|
||||
setContentView(binding.getRoot());
|
||||
setSupportActionBar(binding.toolbar);
|
||||
enableToolbarBackButton();
|
||||
|
||||
TextView copyright = binding.creditsSub;
|
||||
copyright.setText(content.getCopyrightShort());
|
||||
TextView versionHistory = binding.versionHistorySub;
|
||||
versionHistory.setText(content.getVersionHistory());
|
||||
|
||||
binding.versionHistory.setTag("https://catima.app/changelog/");
|
||||
binding.translate.setTag("https://hosted.weblate.org/engage/catima/");
|
||||
binding.license.setTag("https://github.com/CatimaLoyalty/Android/blob/main/LICENSE");
|
||||
binding.repo.setTag("https://github.com/CatimaLoyalty/Android/");
|
||||
binding.privacy.setTag("https://catima.app/privacy-policy/");
|
||||
binding.reportError.setTag("https://github.com/CatimaLoyalty/Android/issues");
|
||||
binding.rate.setTag("https://play.google.com/store/apps/details?id=me.hackerchick.catima");
|
||||
binding.donate.setTag("https://catima.app/donate");
|
||||
|
||||
boolean installedFromGooglePlay = Utils.installedFromGooglePlay(this);
|
||||
// Hide Google Play rate button if not on Google Play
|
||||
binding.rate.setVisibility(installedFromGooglePlay ? View.VISIBLE : View.GONE);
|
||||
// Hide donate button on Google Play (Google Play doesn't allow donation links)
|
||||
binding.donate.setVisibility(installedFromGooglePlay ? View.GONE : View.VISIBLE);
|
||||
|
||||
bindClickListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
content.destroy();
|
||||
clearClickListeners();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
private void bindClickListeners() {
|
||||
binding.versionHistory.setOnClickListener(this::showHistory);
|
||||
binding.translate.setOnClickListener(this::openExternalBrowser);
|
||||
binding.license.setOnClickListener(this::showLicense);
|
||||
binding.repo.setOnClickListener(this::openExternalBrowser);
|
||||
binding.privacy.setOnClickListener(this::showPrivacy);
|
||||
binding.reportError.setOnClickListener(this::openExternalBrowser);
|
||||
binding.rate.setOnClickListener(this::openExternalBrowser);
|
||||
binding.donate.setOnClickListener(this::openExternalBrowser);
|
||||
|
||||
binding.credits.setOnClickListener(view -> showCredits());
|
||||
}
|
||||
|
||||
private void clearClickListeners() {
|
||||
binding.versionHistory.setOnClickListener(null);
|
||||
binding.translate.setOnClickListener(null);
|
||||
binding.license.setOnClickListener(null);
|
||||
binding.repo.setOnClickListener(null);
|
||||
binding.privacy.setOnClickListener(null);
|
||||
binding.reportError.setOnClickListener(null);
|
||||
binding.rate.setOnClickListener(null);
|
||||
binding.donate.setOnClickListener(null);
|
||||
|
||||
binding.credits.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void showCredits() {
|
||||
showHTML(R.string.credits, content.getContributorInfo(), null);
|
||||
}
|
||||
|
||||
private void showHistory(View view) {
|
||||
showHTML(R.string.version_history, content.getHistoryInfo(), view);
|
||||
}
|
||||
|
||||
private void showLicense(View view) {
|
||||
showHTML(R.string.license, content.getLicenseInfo(), view);
|
||||
}
|
||||
|
||||
private void showPrivacy(View view) {
|
||||
showHTML(R.string.privacy_policy, content.getPrivacyInfo(), view);
|
||||
}
|
||||
|
||||
private void showHTML(@StringRes int title, final Spanned text, @Nullable View view) {
|
||||
int dialogContentPadding = getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_padding);
|
||||
TextView textView = new TextView(this);
|
||||
textView.setText(text);
|
||||
Utils.makeTextViewLinksClickable(textView, text);
|
||||
ScrollView scrollView = new ScrollView(this);
|
||||
scrollView.addView(textView);
|
||||
scrollView.setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0);
|
||||
|
||||
// Create dialog
|
||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(this);
|
||||
materialAlertDialogBuilder
|
||||
.setTitle(title)
|
||||
.setView(scrollView)
|
||||
.setPositiveButton(R.string.ok, null);
|
||||
|
||||
// Add View online button if an URL is linked to this view
|
||||
if (view != null && view.getTag() != null) {
|
||||
materialAlertDialogBuilder.setNeutralButton(R.string.view_online, (dialog, which) -> openExternalBrowser(view));
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
materialAlertDialogBuilder.show();
|
||||
}
|
||||
|
||||
private void openExternalBrowser(View view) {
|
||||
Object tag = view.getTag();
|
||||
if (tag instanceof String && ((String) tag).startsWith("https://")) {
|
||||
(new OpenWebLinkHandler()).openBrowser(this, (String) tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package protect.card_locker
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.isVisible
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
import protect.card_locker.databinding.AboutActivityBinding
|
||||
|
||||
class AboutActivity : CatimaAppCompatActivity() {
|
||||
private companion object {
|
||||
private const val TAG = "Catima"
|
||||
}
|
||||
|
||||
private lateinit var binding: AboutActivityBinding
|
||||
private lateinit var content: AboutContent
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = AboutActivityBinding.inflate(layoutInflater)
|
||||
content = AboutContent(this)
|
||||
title = content.pageTitle
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
enableToolbarBackButton()
|
||||
|
||||
binding.apply {
|
||||
creditsSub.text = content.copyrightShort
|
||||
versionHistorySub.text = content.versionHistory
|
||||
|
||||
versionHistory.tag = "https://catima.app/changelog/"
|
||||
translate.tag = "https://hosted.weblate.org/engage/catima/"
|
||||
license.tag = "https://github.com/CatimaLoyalty/Android/blob/main/LICENSE"
|
||||
repo.tag = "https://github.com/CatimaLoyalty/Android/"
|
||||
privacy.tag = "https://catima.app/privacy-policy/"
|
||||
reportError.tag = "https://github.com/CatimaLoyalty/Android/issues"
|
||||
rate.tag = "https://play.google.com/store/apps/details?id=me.hackerchick.catima"
|
||||
donate.tag = "https://catima.app/donate"
|
||||
|
||||
// Hide Google Play rate button if not on Google Play
|
||||
rate.isVisible = BuildConfig.showRateOnGooglePlay
|
||||
// Hide donate button on Google Play (Google Play doesn't allow donation links)
|
||||
donate.isVisible = BuildConfig.showDonate
|
||||
}
|
||||
|
||||
bindClickListeners()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
finish()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
content.destroy()
|
||||
clearClickListeners()
|
||||
}
|
||||
|
||||
private fun bindClickListeners() {
|
||||
binding.apply {
|
||||
versionHistory.setOnClickListener { showHistory(it) }
|
||||
translate.setOnClickListener { openExternalBrowser(it) }
|
||||
license.setOnClickListener { showLicense(it) }
|
||||
repo.setOnClickListener { openExternalBrowser(it) }
|
||||
privacy.setOnClickListener { showPrivacy(it) }
|
||||
reportError.setOnClickListener { openExternalBrowser(it) }
|
||||
rate.setOnClickListener { openExternalBrowser(it) }
|
||||
donate.setOnClickListener { openExternalBrowser(it) }
|
||||
credits.setOnClickListener { showCredits() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearClickListeners() {
|
||||
binding.apply {
|
||||
versionHistory.setOnClickListener(null)
|
||||
translate.setOnClickListener(null)
|
||||
license.setOnClickListener(null)
|
||||
repo.setOnClickListener(null)
|
||||
privacy.setOnClickListener(null)
|
||||
reportError.setOnClickListener(null)
|
||||
rate.setOnClickListener(null)
|
||||
donate.setOnClickListener(null)
|
||||
credits.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCredits() {
|
||||
showHTML(R.string.credits, content.contributorInfo, null)
|
||||
}
|
||||
|
||||
private fun showHistory(view: View) {
|
||||
showHTML(R.string.version_history, content.historyInfo, view)
|
||||
}
|
||||
|
||||
private fun showLicense(view: View) {
|
||||
showHTML(R.string.license, content.licenseInfo, view)
|
||||
}
|
||||
|
||||
private fun showPrivacy(view: View) {
|
||||
showHTML(R.string.privacy_policy, content.privacyInfo, view)
|
||||
}
|
||||
|
||||
private fun showHTML(@StringRes title: Int, text: Spanned, view: View?) {
|
||||
val dialogContentPadding = resources.getDimensionPixelSize(R.dimen.alert_dialog_content_padding)
|
||||
val textView = TextView(this).apply {
|
||||
setText(text)
|
||||
Utils.makeTextViewLinksClickable(this, text)
|
||||
}
|
||||
|
||||
val scrollView = ScrollView(this).apply {
|
||||
addView(textView)
|
||||
setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(this).apply {
|
||||
setTitle(title)
|
||||
setView(scrollView)
|
||||
setPositiveButton(R.string.ok, null)
|
||||
|
||||
// Add View online button if an URL is linked to this view
|
||||
view?.tag?.let {
|
||||
setNeutralButton(R.string.view_online) { _, _ -> openExternalBrowser(view) }
|
||||
}
|
||||
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openExternalBrowser(view: View) {
|
||||
val tag = view.tag
|
||||
if (tag is String && tag.startsWith("https://")) {
|
||||
OpenWebLinkHandler().openBrowser(this, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class BarcodeSelectorActivity extends CatimaAppCompatActivity implements
|
||||
});
|
||||
|
||||
final Bundle b = getIntent().getExtras();
|
||||
final String initialCardId = b != null ? b.getString(LoyaltyCard.BUNDLE_LOYALTY_CARD_CARD_ID) : null;
|
||||
final String initialCardId = b != null ? b.getString("initialCardId") : null;
|
||||
|
||||
if (initialCardId != null) {
|
||||
cardId.setText(initialCardId);
|
||||
|
||||
23
app/src/main/java/protect/card_locker/BarcodeValues.java
Normal file
23
app/src/main/java/protect/card_locker/BarcodeValues.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package protect.card_locker;
|
||||
|
||||
public class BarcodeValues {
|
||||
private final String mFormat;
|
||||
private final String mContent;
|
||||
|
||||
public BarcodeValues(String format, String content) {
|
||||
mFormat = format;
|
||||
mContent = content;
|
||||
}
|
||||
|
||||
public String format() {
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
public String content() {
|
||||
return mContent;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mFormat == null && mContent == null;
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,15 @@ import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import protect.card_locker.databinding.CardShortcutConfigureActivityBinding;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
import protect.card_locker.databinding.SimpleToolbarListActivityBinding;
|
||||
|
||||
/**
|
||||
* The configuration screen for creating a shortcut.
|
||||
*/
|
||||
public class CardShortcutConfigure extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
|
||||
private CardShortcutConfigureActivityBinding binding;
|
||||
private SimpleToolbarListActivityBinding binding;
|
||||
static final String TAG = "Catima";
|
||||
private SQLiteDatabase mDatabase;
|
||||
private LoyaltyCardCursorAdapter mAdapter;
|
||||
@@ -28,7 +28,7 @@ public class CardShortcutConfigure extends CatimaAppCompatActivity implements Lo
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
binding = CardShortcutConfigureActivityBinding.inflate(getLayoutInflater());
|
||||
binding = SimpleToolbarListActivityBinding.inflate(getLayoutInflater());
|
||||
mDatabase = new DBHelper(this).getReadableDatabase();
|
||||
|
||||
// Set the result to CANCELED. This will cause nothing to happen if the
|
||||
@@ -47,26 +47,21 @@ public class CardShortcutConfigure extends CatimaAppCompatActivity implements Lo
|
||||
finish();
|
||||
}
|
||||
|
||||
final RecyclerView cardList = binding.list;
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) cardList.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.setSpanCount(getResources().getInteger(R.integer.main_view_card_columns));
|
||||
}
|
||||
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.All);
|
||||
mAdapter = new LoyaltyCardCursorAdapter(this, cardCursor, this, null);
|
||||
binding.list.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
var layoutManager = (GridLayoutManager) binding.list.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
var settings = new Settings(this);
|
||||
layoutManager.setSpanCount(settings.getPreferredColumnCount());
|
||||
}
|
||||
cardList.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
private void onClickAction(int position) {
|
||||
Cursor selected = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.All);
|
||||
selected.moveToPosition(position);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.fromCursor(CardShortcutConfigure.this, selected);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected);
|
||||
|
||||
Log.d(TAG, "Creating shortcut for card " + loyaltyCard.store + "," + loyaltyCard.id);
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||
Cursor loyaltyCardCursor = DBHelper.getLoyaltyCardCursor(mDatabase, DBHelper.LoyaltyCardArchiveFilter.Unarchived);
|
||||
return subscriber -> {
|
||||
while (loyaltyCardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.fromCursor(this, loyaltyCardCursor);
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(loyaltyCardCursor);
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(LoyaltyCardViewActivity.BUNDLE_ID, card.id);
|
||||
.putExtra("id", card.id);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
subscriber.onNext(
|
||||
new Control.StatelessBuilder(PREFIX + card.id, pendingIntent)
|
||||
@@ -69,11 +69,11 @@ public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||
for (String controlId : controlIds) {
|
||||
Control control;
|
||||
Integer cardId = this.controlIdToCardId(controlId);
|
||||
LoyaltyCard card = DBHelper.getLoyaltyCard(this, mDatabase, cardId);
|
||||
LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, cardId);
|
||||
if (card != null) {
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(LoyaltyCardViewActivity.BUNDLE_ID, card.id);
|
||||
.putExtra("id", card.id);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), card.id, openIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
control = new Control.StatefulBuilder(controlId, pendingIntent)
|
||||
.setTitle(card.store)
|
||||
@@ -99,7 +99,7 @@ public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||
}
|
||||
|
||||
private Bitmap getIcon(Context context, LoyaltyCard loyaltyCard) {
|
||||
Bitmap cardIcon = loyaltyCard.getImageThumbnail(context);
|
||||
Bitmap cardIcon = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
|
||||
|
||||
if (cardIcon != null) {
|
||||
return cardIcon;
|
||||
@@ -129,7 +129,7 @@ public class CardsOnPowerScreenService extends ControlsProviderService {
|
||||
consumer.accept(ControlAction.RESPONSE_OK);
|
||||
Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(LoyaltyCardViewActivity.BUNDLE_ID, controlIdToCardId(controlId));
|
||||
.putExtra("id", controlIdToCardId(controlId));
|
||||
startActivity(openIntent);
|
||||
|
||||
closePowerScreenOnAndroid11();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -47,15 +45,15 @@ public class CatimaBarcode {
|
||||
mBarcodeFormat = barcodeFormat;
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromBarcode(@NonNull BarcodeFormat barcodeFormat) {
|
||||
public static CatimaBarcode fromBarcode(BarcodeFormat barcodeFormat) {
|
||||
return new CatimaBarcode(barcodeFormat);
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromName(@NonNull String name) {
|
||||
public static CatimaBarcode fromName(String name) {
|
||||
return new CatimaBarcode(BarcodeFormat.valueOf(name));
|
||||
}
|
||||
|
||||
public static CatimaBarcode fromPrettyName(@NonNull String prettyName) {
|
||||
public static CatimaBarcode fromPrettyName(String prettyName) {
|
||||
try {
|
||||
return new CatimaBarcode(barcodeFormats.get(barcodePrettyNames.indexOf(prettyName)));
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
|
||||
@@ -4,24 +4,22 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import com.journeyapps.barcodescanner.CaptureManager;
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
|
||||
public class CatimaCaptureManager extends CaptureManager {
|
||||
private final Consumer<String> mErrorCallback;
|
||||
private final Context mContext;
|
||||
|
||||
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView, Consumer<String> errorCallback) {
|
||||
public CatimaCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
|
||||
super(activity, barcodeView);
|
||||
|
||||
mErrorCallback = errorCallback;
|
||||
mContext = activity.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void displayFrameworkBugMessageAndExit(String message) {
|
||||
// We don't want to exit, as we also have a enter from card image and add manually button here
|
||||
// So, instead, we call our error callback
|
||||
mErrorCallback.accept(message);
|
||||
// So we show a toast instead
|
||||
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
public enum LoyaltyCardOrder {
|
||||
Alpha,
|
||||
LastUsed,
|
||||
ValidFrom,
|
||||
Expiry
|
||||
}
|
||||
|
||||
@@ -333,10 +332,10 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
Set<String> files = new HashSet<>();
|
||||
Cursor cardCursor = getLoyaltyCardCursor(database);
|
||||
while (cardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.fromCursor(context, cardCursor);
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
String name = Utils.getCardImageFileName(card.id, imageLocationType);
|
||||
if (card.getImageForImageLocationType(context, imageLocationType) != null) {
|
||||
if (Utils.retrieveCardImageAsFile(context, name).exists()) {
|
||||
files.add(name);
|
||||
}
|
||||
}
|
||||
@@ -536,14 +535,14 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return (rowsUpdated == 1);
|
||||
}
|
||||
|
||||
public static LoyaltyCard getLoyaltyCard(Context context, SQLiteDatabase database, final int id) {
|
||||
public static LoyaltyCard getLoyaltyCard(SQLiteDatabase database, final int id) {
|
||||
Cursor data = database.query(LoyaltyCardDbIds.TABLE, null, whereAttrs(LoyaltyCardDbIds.ID), withArgs(id), null, null, null);
|
||||
|
||||
LoyaltyCard card = null;
|
||||
|
||||
if (data.getCount() == 1) {
|
||||
data.moveToFirst();
|
||||
card = LoyaltyCard.fromCursor(context, data);
|
||||
card = LoyaltyCard.toLoyaltyCard(data);
|
||||
}
|
||||
|
||||
data.close();
|
||||
@@ -917,10 +916,6 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
return LoyaltyCardDbIds.LAST_USED;
|
||||
}
|
||||
|
||||
if (order == LoyaltyCardOrder.ValidFrom) {
|
||||
return LoyaltyCardDbIds.VALID_FROM;
|
||||
}
|
||||
|
||||
if (order == LoyaltyCardOrder.Expiry) {
|
||||
return LoyaltyCardDbIds.EXPIRY;
|
||||
}
|
||||
|
||||
@@ -9,11 +9,9 @@ import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
@@ -23,7 +21,6 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -81,21 +78,15 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
Log.e(TAG, "Activity returned NULL uri");
|
||||
return;
|
||||
}
|
||||
// Running this in a thread prevents Android from throwing a NetworkOnMainThreadException for large files
|
||||
// FIXME: This is still suboptimal, because showing that the export started is delayed until the network request finishes
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
OutputStream writer = getContentResolver().openOutputStream(uri);
|
||||
Log.d(TAG, "Starting file export with: " + result);
|
||||
startExport(writer, uri, exportPassword.toCharArray(), true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to export file: " + result, e);
|
||||
onExportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, result.toString()), uri);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
OutputStream writer = getContentResolver().openOutputStream(uri);
|
||||
Log.e(TAG, "Starting file export with: " + result.toString());
|
||||
startExport(writer, uri, exportPassword.toCharArray(), true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to export file: " + result.toString(), e);
|
||||
onExportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, result.toString()), uri);
|
||||
}
|
||||
|
||||
});
|
||||
fileOpenLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), result -> {
|
||||
if (result == null) {
|
||||
@@ -130,19 +121,16 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
builder.setTitle(R.string.exportPassword);
|
||||
|
||||
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
||||
|
||||
final TextInputLayout textInputLayout = new TextInputLayout(ImportExportActivity.this);
|
||||
textInputLayout.setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.setMargins(50, 10, 50, 0);
|
||||
textInputLayout.setLayoutParams(params);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = 50;
|
||||
params.rightMargin = 50;
|
||||
|
||||
final EditText input = new EditText(ImportExportActivity.this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
input.setLayoutParams(params);
|
||||
input.setHint(R.string.exportPasswordHint);
|
||||
|
||||
textInputLayout.addView(input);
|
||||
container.addView(textInputLayout);
|
||||
container.addView(input);
|
||||
builder.setView(container);
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
exportPassword = input.getText().toString();
|
||||
@@ -155,6 +143,7 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.cancel());
|
||||
builder.show();
|
||||
|
||||
});
|
||||
|
||||
// Check that there is a file manager available
|
||||
@@ -164,28 +153,17 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
// Check that there is an app that data can be imported from
|
||||
Button importApplication = binding.importOptionApplicationButton;
|
||||
importApplication.setOnClickListener(v -> chooseImportType(true, null));
|
||||
|
||||
// FIXME: The importer/exporter is currently quite broken
|
||||
// To prevent the screen from turning off during import/export and some devices killing Catima as it's no longer foregrounded, force the screen to stay on here
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
private void openFileForImport(Uri uri, char[] password) {
|
||||
// Running this in a thread prevents Android from throwing a NetworkOnMainThreadException for large files
|
||||
// FIXME: This is still suboptimal, because showing that the import started is delayed until the network request finishes
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
InputStream reader = getContentResolver().openInputStream(uri);
|
||||
Log.d(TAG, "Starting file import with: " + uri);
|
||||
startImport(reader, uri, importDataFormat, password, true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to import file: " + uri, e);
|
||||
onImportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, e.toString()), uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
InputStream reader = getContentResolver().openInputStream(uri);
|
||||
Log.e(TAG, "Starting file import with: " + uri.toString());
|
||||
startImport(reader, uri, importDataFormat, password, true);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
|
||||
onImportComplete(new ImportExportResult(ImportExportResultType.GenericFailure, e.toString()), uri, importDataFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private void chooseImportType(boolean choosePicker,
|
||||
@@ -337,21 +315,9 @@ public class ImportExportActivity extends CatimaAppCompatActivity {
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
||||
builder.setTitle(R.string.passwordRequired);
|
||||
|
||||
FrameLayout container = new FrameLayout(ImportExportActivity.this);
|
||||
|
||||
final TextInputLayout textInputLayout = new TextInputLayout(ImportExportActivity.this);
|
||||
textInputLayout.setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.setMargins(50, 10, 50, 0);
|
||||
textInputLayout.setLayoutParams(params);
|
||||
|
||||
final EditText input = new EditText(ImportExportActivity.this);
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
input.setHint(R.string.exportPasswordHint);
|
||||
|
||||
textInputLayout.addView(input);
|
||||
container.addView(textInputLayout);
|
||||
builder.setView(container);
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
openFileForImport(uri, input.getText().toString().toCharArray());
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -91,16 +90,16 @@ public class ImportExportTask implements CompatCallable<ImportExportResult> {
|
||||
progress = new ProgressDialog(activity);
|
||||
progress.setTitle(doImport ? R.string.importing : R.string.exporting);
|
||||
|
||||
progress.setOnCancelListener(dialog -> cancel());
|
||||
progress.setOnDismissListener(dialog -> cancel());
|
||||
progress.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
ImportExportTask.this.stop();
|
||||
}
|
||||
});
|
||||
|
||||
progress.show();
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
ImportExportTask.this.stop();
|
||||
}
|
||||
|
||||
protected ImportExportResult doInBackground(Void... nothing) {
|
||||
final SQLiteDatabase database = new DBHelper(activity).getWritableDatabase();
|
||||
ImportExportResult result;
|
||||
|
||||
@@ -125,29 +125,7 @@ public class ImportURIHelper {
|
||||
headerColor = Integer.parseInt(unparsedHeaderColor);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(
|
||||
-1,
|
||||
store,
|
||||
note,
|
||||
validFrom,
|
||||
expiry,
|
||||
balance,
|
||||
balanceType,
|
||||
cardId,
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
0,
|
||||
Utils.getUnixTime(),
|
||||
100,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
return new LoyaltyCard(-1, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, 0, Utils.getUnixTime(), 100, 0);
|
||||
} catch (NumberFormatException | UnsupportedEncodingException | ArrayIndexOutOfBoundsException ex) {
|
||||
throw new InvalidObjectException("Not a valid import URI");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -11,551 +10,141 @@ import androidx.annotation.Nullable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class LoyaltyCard {
|
||||
public int id;
|
||||
public String store;
|
||||
public String note;
|
||||
public class LoyaltyCard implements Parcelable {
|
||||
public final int id;
|
||||
public final String store;
|
||||
public final String note;
|
||||
@Nullable
|
||||
public Date validFrom;
|
||||
public final Date validFrom;
|
||||
@Nullable
|
||||
public Date expiry;
|
||||
public BigDecimal balance;
|
||||
public final Date expiry;
|
||||
public final BigDecimal balance;
|
||||
@Nullable
|
||||
public Currency balanceType;
|
||||
public String cardId;
|
||||
public final Currency balanceType;
|
||||
public final String cardId;
|
||||
@Nullable
|
||||
public String barcodeId;
|
||||
public final String barcodeId;
|
||||
@Nullable
|
||||
public CatimaBarcode barcodeType;
|
||||
public final CatimaBarcode barcodeType;
|
||||
@Nullable
|
||||
public Integer headerColor;
|
||||
public int starStatus;
|
||||
public long lastUsed;
|
||||
public final Integer headerColor;
|
||||
public final int starStatus;
|
||||
public final int archiveStatus;
|
||||
public final long lastUsed;
|
||||
public int zoomLevel;
|
||||
public int archiveStatus;
|
||||
|
||||
@Nullable
|
||||
private Bitmap imageThumbnail;
|
||||
@Nullable
|
||||
private String imageThumbnailPath;
|
||||
@Nullable
|
||||
private Bitmap imageFront;
|
||||
@Nullable
|
||||
private String imageFrontPath;
|
||||
@Nullable
|
||||
private Bitmap imageBack;
|
||||
@Nullable
|
||||
private String imageBackPath;
|
||||
|
||||
public static final String BUNDLE_LOYALTY_CARD_ID = "loyaltyCardId";
|
||||
public static final String BUNDLE_LOYALTY_CARD_STORE = "loyaltyCardStore";
|
||||
public static final String BUNDLE_LOYALTY_CARD_NOTE = "loyaltyCardNote";
|
||||
public static final String BUNDLE_LOYALTY_CARD_VALID_FROM = "loyaltyCardValidFrom";
|
||||
public static final String BUNDLE_LOYALTY_CARD_EXPIRY = "loyaltyCardExpiry";
|
||||
public static final String BUNDLE_LOYALTY_CARD_BALANCE = "loyaltyCardBalance";
|
||||
public static final String BUNDLE_LOYALTY_CARD_BALANCE_TYPE = "loyaltyCardBalanceType";
|
||||
public static final String BUNDLE_LOYALTY_CARD_CARD_ID = "loyaltyCardCardId";
|
||||
public static final String BUNDLE_LOYALTY_CARD_BARCODE_ID = "loyaltyCardBarcodeId";
|
||||
public static final String BUNDLE_LOYALTY_CARD_BARCODE_TYPE = "loyaltyCardBarcodeType";
|
||||
public static final String BUNDLE_LOYALTY_CARD_HEADER_COLOR = "loyaltyCardHeaderColor";
|
||||
public static final String BUNDLE_LOYALTY_CARD_STAR_STATUS = "loyaltyCardStarStatus";
|
||||
public static final String BUNDLE_LOYALTY_CARD_LAST_USED = "loyaltyCardLastUsed";
|
||||
public static final String BUNDLE_LOYALTY_CARD_ZOOM_LEVEL = "loyaltyCardZoomLevel";
|
||||
public static final String BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS = "loyaltyCardArchiveStatus";
|
||||
public static final String BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL = "loyaltyCardImageThumbnail";
|
||||
public static final String BUNDLE_LOYALTY_CARD_IMAGE_FRONT = "loyaltyCardImageFront";
|
||||
public static final String BUNDLE_LOYALTY_CARD_IMAGE_BACK = "loyaltyCardImageBack";
|
||||
|
||||
private static final String TEMP_IMAGE_THUMBNAIL_FILE_NAME = "loyaltyCardTempImageThumbnailFileName";
|
||||
private static final String TEMP_IMAGE_FRONT_FILE_NAME = "loyaltyCardTempImageFrontFileName";
|
||||
private static final String TEMP_IMAGE_BACK_FILE_NAME = "loyaltyCardTempImageBackFileName";
|
||||
|
||||
/**
|
||||
* Create a loyalty card object with default values
|
||||
*/
|
||||
public LoyaltyCard() {
|
||||
setId(-1);
|
||||
setStore("");
|
||||
setNote("");
|
||||
setValidFrom(null);
|
||||
setExpiry(null);
|
||||
setBalance(new BigDecimal("0"));
|
||||
setBalanceType(null);
|
||||
setCardId("");
|
||||
setBarcodeId(null);
|
||||
setBarcodeType(null);
|
||||
setHeaderColor(null);
|
||||
setStarStatus(0);
|
||||
setLastUsed(Utils.getUnixTime());
|
||||
setZoomLevel(100);
|
||||
setArchiveStatus(0);
|
||||
setImageThumbnail(null, null);
|
||||
setImageFront(null, null);
|
||||
setImageBack(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new loyalty card
|
||||
*
|
||||
* @param id
|
||||
* @param store
|
||||
* @param note
|
||||
* @param validFrom
|
||||
* @param expiry
|
||||
* @param balance
|
||||
* @param balanceType
|
||||
* @param cardId
|
||||
* @param barcodeId
|
||||
* @param barcodeType
|
||||
* @param headerColor
|
||||
* @param starStatus
|
||||
* @param lastUsed
|
||||
* @param zoomLevel
|
||||
* @param archiveStatus
|
||||
*/
|
||||
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,
|
||||
@Nullable Bitmap imageThumbnail, @Nullable String imageThumbnailPath,
|
||||
@Nullable Bitmap imageFront, @Nullable String imageFrontPath,
|
||||
@Nullable Bitmap imageBack, @Nullable String imageBackPath) {
|
||||
setId(id);
|
||||
setStore(store);
|
||||
setNote(note);
|
||||
setValidFrom(validFrom);
|
||||
setExpiry(expiry);
|
||||
setBalance(balance);
|
||||
setBalanceType(balanceType);
|
||||
setCardId(cardId);
|
||||
setBarcodeId(barcodeId);
|
||||
setBarcodeType(barcodeType);
|
||||
setHeaderColor(headerColor);
|
||||
setStarStatus(starStatus);
|
||||
setLastUsed(lastUsed);
|
||||
setZoomLevel(zoomLevel);
|
||||
setArchiveStatus(archiveStatus);
|
||||
setImageThumbnail(imageThumbnail, imageThumbnailPath);
|
||||
setImageFront(imageFront, imageFrontPath);
|
||||
setImageBack(imageBack, imageBackPath);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bitmap getImageThumbnail(Context context) {
|
||||
if (imageThumbnailPath != null) {
|
||||
if (imageThumbnailPath.equals(TEMP_IMAGE_THUMBNAIL_FILE_NAME)) {
|
||||
imageThumbnail = Utils.loadTempImage(context, imageThumbnailPath);
|
||||
} else {
|
||||
imageThumbnail = Utils.retrieveCardImage(context, imageThumbnailPath);
|
||||
}
|
||||
imageThumbnailPath = null;
|
||||
}
|
||||
|
||||
if (imageThumbnail == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return imageThumbnail.copy(imageThumbnail.getConfig(), imageThumbnail.isMutable());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bitmap getImageFront(Context context) {
|
||||
if (imageFrontPath != null) {
|
||||
if (imageFrontPath.equals(TEMP_IMAGE_FRONT_FILE_NAME)) {
|
||||
imageFront = Utils.loadTempImage(context, imageFrontPath);
|
||||
} else {
|
||||
imageFront = Utils.retrieveCardImage(context, imageFrontPath);
|
||||
}
|
||||
imageFrontPath = null;
|
||||
}
|
||||
|
||||
if (imageFront == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return imageFront.copy(imageFront.getConfig(), imageFront.isMutable());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bitmap getImageBack(Context context) {
|
||||
if (imageBackPath != null) {
|
||||
if (imageBackPath.equals(TEMP_IMAGE_BACK_FILE_NAME)) {
|
||||
imageBack = Utils.loadTempImage(context, imageBackPath);
|
||||
} else {
|
||||
imageBack = Utils.retrieveCardImage(context, imageBackPath);
|
||||
}
|
||||
imageBackPath = null;
|
||||
}
|
||||
|
||||
if (imageBack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return imageBack.copy(imageBack.getConfig(), imageBack.isMutable());
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
final long lastUsed, final int zoomLevel, final int archiveStatus) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setStore(@NonNull String store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public void setNote(@NonNull String note) {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
public void setValidFrom(@Nullable Date validFrom) {
|
||||
this.validFrom = validFrom;
|
||||
}
|
||||
|
||||
public void setExpiry(@Nullable Date expiry) {
|
||||
this.expiry = expiry;
|
||||
}
|
||||
|
||||
public void setBalance(@NonNull BigDecimal balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public void setBalanceType(@Nullable Currency balanceType) {
|
||||
this.balanceType = balanceType;
|
||||
}
|
||||
|
||||
public void setCardId(@NonNull String cardId) {
|
||||
this.cardId = cardId;
|
||||
}
|
||||
|
||||
public void setBarcodeId(@Nullable String barcodeId) {
|
||||
this.barcodeId = barcodeId;
|
||||
}
|
||||
|
||||
public void setBarcodeType(@Nullable CatimaBarcode barcodeType) {
|
||||
this.barcodeType = barcodeType;
|
||||
}
|
||||
|
||||
public void setHeaderColor(@Nullable Integer headerColor) {
|
||||
this.headerColor = headerColor;
|
||||
}
|
||||
|
||||
public void setStarStatus(int starStatus) {
|
||||
if (starStatus != 0 && starStatus != 1) {
|
||||
throw new IllegalArgumentException("starStatus must be 0 or 1");
|
||||
}
|
||||
|
||||
this.starStatus = starStatus;
|
||||
}
|
||||
|
||||
public void setLastUsed(long lastUsed) {
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
public void setZoomLevel(int zoomLevel) {
|
||||
if (zoomLevel < 0 || zoomLevel > 100) {
|
||||
throw new IllegalArgumentException("zoomLevel must be in range 0-100");
|
||||
}
|
||||
|
||||
this.zoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
public void setArchiveStatus(int archiveStatus) {
|
||||
if (archiveStatus != 0 && archiveStatus != 1) {
|
||||
throw new IllegalArgumentException("archiveStatus must be 0 or 1");
|
||||
}
|
||||
|
||||
this.archiveStatus = archiveStatus;
|
||||
}
|
||||
|
||||
public void setImageThumbnail(@Nullable Bitmap imageThumbnail, @Nullable String imageThumbnailPath) {
|
||||
if (imageThumbnail != null && imageThumbnailPath != null) {
|
||||
throw new IllegalArgumentException("Cannot set both thumbnail and path");
|
||||
}
|
||||
|
||||
this.imageThumbnailPath = imageThumbnailPath;
|
||||
this.imageThumbnail = imageThumbnail != null ? imageThumbnail.copy(imageThumbnail.getConfig(), imageThumbnail.isMutable()) : null;
|
||||
protected LoyaltyCard(Parcel in) {
|
||||
id = in.readInt();
|
||||
store = in.readString();
|
||||
note = in.readString();
|
||||
long tmpValidFrom = in.readLong();
|
||||
validFrom = tmpValidFrom != -1 ? new Date(tmpValidFrom) : null;
|
||||
long tmpExpiry = in.readLong();
|
||||
expiry = tmpExpiry != -1 ? new Date(tmpExpiry) : null;
|
||||
balance = (BigDecimal) in.readValue(BigDecimal.class.getClassLoader());
|
||||
balanceType = (Currency) in.readValue(Currency.class.getClassLoader());
|
||||
cardId = in.readString();
|
||||
barcodeId = in.readString();
|
||||
String tmpBarcodeType = in.readString();
|
||||
barcodeType = !tmpBarcodeType.isEmpty() ? CatimaBarcode.fromName(tmpBarcodeType) : null;
|
||||
int tmpHeaderColor = in.readInt();
|
||||
headerColor = tmpHeaderColor != -1 ? tmpHeaderColor : null;
|
||||
starStatus = in.readInt();
|
||||
lastUsed = in.readLong();
|
||||
zoomLevel = in.readInt();
|
||||
archiveStatus = in.readInt();
|
||||
}
|
||||
|
||||
public void setImageFront(@Nullable Bitmap imageFront, @Nullable String imageFrontPath) {
|
||||
if (imageFront != null && imageFrontPath != null) {
|
||||
throw new IllegalArgumentException("Cannot set both thumbnail and path");
|
||||
}
|
||||
|
||||
this.imageFrontPath = imageFrontPath;
|
||||
this.imageFront = imageFront != null ? imageFront.copy(imageFront.getConfig(), imageFront.isMutable()) : null;
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeInt(id);
|
||||
parcel.writeString(store);
|
||||
parcel.writeString(note);
|
||||
parcel.writeLong(validFrom != null ? validFrom.getTime() : -1);
|
||||
parcel.writeLong(expiry != null ? expiry.getTime() : -1);
|
||||
parcel.writeValue(balance);
|
||||
parcel.writeValue(balanceType);
|
||||
parcel.writeString(cardId);
|
||||
parcel.writeString(barcodeId);
|
||||
parcel.writeString(barcodeType != null ? barcodeType.name() : "");
|
||||
parcel.writeInt(headerColor != null ? headerColor : -1);
|
||||
parcel.writeInt(starStatus);
|
||||
parcel.writeLong(lastUsed);
|
||||
parcel.writeInt(zoomLevel);
|
||||
parcel.writeInt(archiveStatus);
|
||||
}
|
||||
|
||||
public void setImageBack(@Nullable Bitmap imageBack, @Nullable String imageBackPath) {
|
||||
if (imageBack != null && imageBackPath != null) {
|
||||
throw new IllegalArgumentException("Cannot set both thumbnail and path");
|
||||
}
|
||||
|
||||
this.imageBackPath = imageBackPath;
|
||||
this.imageBack = imageBack != null ? imageBack.copy(imageBack.getConfig(), imageBack.isMutable()) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bitmap getImageForImageLocationType(Context context, ImageLocationType imageLocationType) {
|
||||
if (imageLocationType == ImageLocationType.icon) {
|
||||
return getImageThumbnail(context);
|
||||
} else if (imageLocationType == ImageLocationType.front) {
|
||||
return getImageFront(context);
|
||||
} else if (imageLocationType == ImageLocationType.back) {
|
||||
return getImageBack(context);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown image location type");
|
||||
}
|
||||
|
||||
public void updateFromBundle(@NonNull Bundle bundle, boolean requireFull) {
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_ID)) {
|
||||
setId(bundle.getInt(BUNDLE_LOYALTY_CARD_ID));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_ID);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_STORE)) {
|
||||
setStore(Objects.requireNonNull(bundle.getString(BUNDLE_LOYALTY_CARD_STORE)));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_STORE);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_NOTE)) {
|
||||
setNote(Objects.requireNonNull(bundle.getString(BUNDLE_LOYALTY_CARD_NOTE)));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_NOTE);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_VALID_FROM)) {
|
||||
long tmpValidFrom = bundle.getLong(BUNDLE_LOYALTY_CARD_VALID_FROM);
|
||||
setValidFrom(tmpValidFrom > 0 ? new Date(tmpValidFrom) : null);
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_VALID_FROM);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_EXPIRY)) {
|
||||
long tmpExpiry = bundle.getLong(BUNDLE_LOYALTY_CARD_EXPIRY);
|
||||
setExpiry(tmpExpiry > 0 ? new Date(tmpExpiry) : null);
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_EXPIRY);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_BALANCE)) {
|
||||
setBalance(new BigDecimal(bundle.getString(BUNDLE_LOYALTY_CARD_BALANCE)));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BALANCE);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_BALANCE_TYPE)) {
|
||||
String tmpBalanceType = bundle.getString(BUNDLE_LOYALTY_CARD_BALANCE_TYPE);
|
||||
setBalanceType(tmpBalanceType != null ? Currency.getInstance(tmpBalanceType) : null);
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BALANCE_TYPE);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_CARD_ID)) {
|
||||
setCardId(Objects.requireNonNull(bundle.getString(BUNDLE_LOYALTY_CARD_CARD_ID)));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_CARD_ID);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_BARCODE_ID)) {
|
||||
setBarcodeId(bundle.getString(BUNDLE_LOYALTY_CARD_BARCODE_ID));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BARCODE_ID);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_BARCODE_TYPE)) {
|
||||
String tmpBarcodeType = bundle.getString(BUNDLE_LOYALTY_CARD_BARCODE_TYPE);
|
||||
setBarcodeType(tmpBarcodeType != null ? CatimaBarcode.fromName(tmpBarcodeType) : null);
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_BARCODE_TYPE);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_HEADER_COLOR)) {
|
||||
int tmpHeaderColor = bundle.getInt(BUNDLE_LOYALTY_CARD_HEADER_COLOR);
|
||||
setHeaderColor(tmpHeaderColor != -1 ? tmpHeaderColor : null);
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_HEADER_COLOR);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_STAR_STATUS)) {
|
||||
setStarStatus(bundle.getInt(BUNDLE_LOYALTY_CARD_STAR_STATUS));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_STAR_STATUS);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_LAST_USED)) {
|
||||
setLastUsed(bundle.getLong(BUNDLE_LOYALTY_CARD_LAST_USED));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_LAST_USED);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_ZOOM_LEVEL)) {
|
||||
setZoomLevel(bundle.getInt(BUNDLE_LOYALTY_CARD_ZOOM_LEVEL));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_ZOOM_LEVEL);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS)) {
|
||||
setArchiveStatus(bundle.getInt(BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL)) {
|
||||
setImageThumbnail(null, bundle.getString(BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_IMAGE_FRONT)) {
|
||||
setImageFront(null, bundle.getString(BUNDLE_LOYALTY_CARD_IMAGE_FRONT));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_IMAGE_FRONT);
|
||||
}
|
||||
if (bundle.containsKey(BUNDLE_LOYALTY_CARD_IMAGE_BACK)) {
|
||||
setImageBack(null, bundle.getString(BUNDLE_LOYALTY_CARD_IMAGE_BACK));
|
||||
} else if (requireFull) {
|
||||
throw new IllegalArgumentException("Missing key " + BUNDLE_LOYALTY_CARD_IMAGE_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
public Bundle toBundle(Context context, List<String> exportLimit) {
|
||||
boolean exportIsLimited = !exportLimit.isEmpty();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_ID)) {
|
||||
bundle.putInt(BUNDLE_LOYALTY_CARD_ID, id);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_STORE)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_STORE, store);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_NOTE)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_NOTE, note);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_VALID_FROM)) {
|
||||
bundle.putLong(BUNDLE_LOYALTY_CARD_VALID_FROM, validFrom != null ? validFrom.getTime() : -1);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_EXPIRY)) {
|
||||
bundle.putLong(BUNDLE_LOYALTY_CARD_EXPIRY, expiry != null ? expiry.getTime() : -1);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BALANCE)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_BALANCE, balance.toString());
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BALANCE_TYPE)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_BALANCE_TYPE, balanceType != null ? balanceType.toString() : null);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_CARD_ID)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_CARD_ID, cardId);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BARCODE_ID)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_BARCODE_ID, barcodeId);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_BARCODE_TYPE)) {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_BARCODE_TYPE, barcodeType != null ? barcodeType.name() : null);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_HEADER_COLOR)) {
|
||||
bundle.putInt(BUNDLE_LOYALTY_CARD_HEADER_COLOR, headerColor != null ? headerColor : -1);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_STAR_STATUS)) {
|
||||
bundle.putInt(BUNDLE_LOYALTY_CARD_STAR_STATUS, starStatus);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_LAST_USED)) {
|
||||
bundle.putLong(BUNDLE_LOYALTY_CARD_LAST_USED, lastUsed);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_ZOOM_LEVEL)) {
|
||||
bundle.putInt(BUNDLE_LOYALTY_CARD_ZOOM_LEVEL, zoomLevel);
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS)) {
|
||||
bundle.putInt(BUNDLE_LOYALTY_CARD_ARCHIVE_STATUS, archiveStatus);
|
||||
}
|
||||
// There is an (undocumented) size limit to bundles of around 2MB(?), when going over it you will experience a random crash
|
||||
// So, instead of storing the bitmaps directly, we write the bitmap to a temp file and store the path
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL)) {
|
||||
Bitmap thumbnail = getImageThumbnail(context);
|
||||
if (thumbnail != null) {
|
||||
Utils.saveTempImage(context, thumbnail, TEMP_IMAGE_THUMBNAIL_FILE_NAME, Bitmap.CompressFormat.PNG);
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL, TEMP_IMAGE_THUMBNAIL_FILE_NAME);
|
||||
} else {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_THUMBNAIL, null);
|
||||
}
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_IMAGE_FRONT)) {
|
||||
Bitmap front = getImageFront(context);
|
||||
if (front != null) {
|
||||
Utils.saveTempImage(context, front, TEMP_IMAGE_FRONT_FILE_NAME, Bitmap.CompressFormat.PNG);
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_FRONT, TEMP_IMAGE_FRONT_FILE_NAME);
|
||||
} else {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_FRONT, null);
|
||||
}
|
||||
}
|
||||
if (!exportIsLimited || exportLimit.contains(BUNDLE_LOYALTY_CARD_IMAGE_BACK)) {
|
||||
Bitmap back = getImageBack(context);
|
||||
if (back != null) {
|
||||
Utils.saveTempImage(context, back, TEMP_IMAGE_BACK_FILE_NAME, Bitmap.CompressFormat.PNG);
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_BACK, TEMP_IMAGE_BACK_FILE_NAME);
|
||||
} else {
|
||||
bundle.putString(BUNDLE_LOYALTY_CARD_IMAGE_BACK, null);
|
||||
}
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static LoyaltyCard fromCursor(Context context, Cursor cursor) {
|
||||
// id
|
||||
public static LoyaltyCard toLoyaltyCard(Cursor cursor) {
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
// store
|
||||
String store = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STORE));
|
||||
// note
|
||||
String note = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.NOTE));
|
||||
// validFrom
|
||||
long validFromLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.VALID_FROM));
|
||||
Date validFrom = validFromLong > 0 ? new Date(validFromLong) : null;
|
||||
// expiry
|
||||
long expiryLong = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.EXPIRY));
|
||||
Date expiry = expiryLong > 0 ? new Date(expiryLong) : null;
|
||||
// balance
|
||||
BigDecimal balance = new BigDecimal(cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE)));
|
||||
// balanceType
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
Currency balanceType = !cursor.isNull(balanceTypeColumn) ? Currency.getInstance(cursor.getString(balanceTypeColumn)) : null;
|
||||
// cardId
|
||||
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.CARD_ID));
|
||||
// barcodeId
|
||||
int barcodeIdColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID);
|
||||
String barcodeId = !cursor.isNull(barcodeIdColumn) ? cursor.getString(barcodeIdColumn) : null;
|
||||
// barcodeType
|
||||
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
|
||||
CatimaBarcode barcodeType = !cursor.isNull(barcodeTypeColumn) ? CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn)) : null;
|
||||
// headerColor
|
||||
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
|
||||
Integer headerColor = !cursor.isNull(headerColorColumn) ? cursor.getInt(headerColorColumn) : null;
|
||||
// starStatus
|
||||
int starStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
|
||||
// lastUsed
|
||||
String barcodeId = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_ID));
|
||||
int starred = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.STAR_STATUS));
|
||||
long lastUsed = cursor.getLong(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.LAST_USED));
|
||||
// zoomLevel
|
||||
int zoomLevel = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ZOOM_LEVEL));
|
||||
// archiveStatus
|
||||
int archiveStatus = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ARCHIVE_STATUS));
|
||||
int archived = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ARCHIVE_STATUS));
|
||||
|
||||
return new LoyaltyCard(
|
||||
id,
|
||||
store,
|
||||
note,
|
||||
validFrom,
|
||||
expiry,
|
||||
balance,
|
||||
balanceType,
|
||||
cardId,
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
starStatus,
|
||||
lastUsed,
|
||||
zoomLevel,
|
||||
archiveStatus,
|
||||
null,
|
||||
Utils.getCardImageFileName(id, ImageLocationType.icon),
|
||||
null,
|
||||
Utils.getCardImageFileName(id, ImageLocationType.front),
|
||||
null,
|
||||
Utils.getCardImageFileName(id, ImageLocationType.back)
|
||||
);
|
||||
int barcodeTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BARCODE_TYPE);
|
||||
int balanceTypeColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.BALANCE_TYPE);
|
||||
int headerColorColumn = cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.HEADER_COLOR);
|
||||
|
||||
CatimaBarcode barcodeType = null;
|
||||
Currency balanceType = null;
|
||||
Date validFrom = null;
|
||||
Date expiry = null;
|
||||
Integer headerColor = null;
|
||||
|
||||
if (cursor.isNull(barcodeTypeColumn) == false) {
|
||||
barcodeType = CatimaBarcode.fromName(cursor.getString(barcodeTypeColumn));
|
||||
}
|
||||
|
||||
if (cursor.isNull(balanceTypeColumn) == false) {
|
||||
balanceType = Currency.getInstance(cursor.getString(balanceTypeColumn));
|
||||
}
|
||||
|
||||
if (validFromLong > 0) {
|
||||
validFrom = new Date(validFromLong);
|
||||
}
|
||||
|
||||
if (expiryLong > 0) {
|
||||
expiry = new Date(expiryLong);
|
||||
}
|
||||
|
||||
if (cursor.isNull(headerColorColumn) == false) {
|
||||
headerColor = cursor.getInt(headerColorColumn);
|
||||
}
|
||||
|
||||
return new LoyaltyCard(id, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred, lastUsed, zoomLevel, archived);
|
||||
}
|
||||
|
||||
public static boolean isDuplicate(Context context, final LoyaltyCard a, final LoyaltyCard b) {
|
||||
// Note: Bitmap comparing is slow, be careful when calling this method
|
||||
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
|
||||
@@ -570,23 +159,12 @@ public class LoyaltyCard {
|
||||
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
|
||||
nullableBitmapsEqual(a.getImageThumbnail(context), b.getImageThumbnail(context)) && // nullable Bitmap
|
||||
nullableBitmapsEqual(a.getImageFront(context), b.getImageFront(context)) && // nullable Bitmap
|
||||
nullableBitmapsEqual(a.getImageBack(context), b.getImageBack(context)); // nullable Bitmap
|
||||
a.archiveStatus == b.archiveStatus; // non-nullable int
|
||||
}
|
||||
|
||||
public static boolean nullableBitmapsEqual(@Nullable Bitmap a, @Nullable Bitmap b) {
|
||||
if (a == null && b == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a != null && b != null) {
|
||||
return a.sameAs(b);
|
||||
}
|
||||
|
||||
// One is null and the other isn't, so it's not equal
|
||||
return false;
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -595,8 +173,7 @@ public class LoyaltyCard {
|
||||
return String.format(
|
||||
"LoyaltyCard{%n id=%s,%n store=%s,%n note=%s,%n validFrom=%s,%n expiry=%s,%n"
|
||||
+ " balance=%s,%n balanceType=%s,%n cardId=%s,%n barcodeId=%s,%n barcodeType=%s,%n"
|
||||
+ " headerColor=%s,%n starStatus=%s,%n lastUsed=%s,%n zoomLevel=%s,%n archiveStatus=%s%n"
|
||||
+ " imageThumbnail=%s,%n imageThumbnailPath=%s,%n imageFront=%s,%n imageFrontPath=%s,%n imageBack=%s,%n imageBackPath=%s,%n}",
|
||||
+ " headerColor=%s,%n starStatus=%s,%n lastUsed=%s,%n zoomLevel=%s,%n archiveStatus=%s%n}",
|
||||
this.id,
|
||||
this.store,
|
||||
this.note,
|
||||
@@ -611,13 +188,19 @@ public class LoyaltyCard {
|
||||
this.starStatus,
|
||||
this.lastUsed,
|
||||
this.zoomLevel,
|
||||
this.archiveStatus,
|
||||
this.imageThumbnail,
|
||||
this.imageThumbnailPath,
|
||||
this.imageFront,
|
||||
this.imageFrontPath,
|
||||
this.imageBack,
|
||||
this.imageBackPath
|
||||
this.archiveStatus
|
||||
);
|
||||
}
|
||||
|
||||
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
|
||||
@Override
|
||||
public LoyaltyCard createFromParcel(Parcel in) {
|
||||
return new LoyaltyCard(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoyaltyCard[] newArray(int size) {
|
||||
return new LoyaltyCard[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import protect.card_locker.databinding.LoyaltyCardLayoutBinding;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
|
||||
public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCursorAdapter.LoyaltyCardListItemViewHolder> {
|
||||
private int mCurrentSelectedIndex = -1;
|
||||
@@ -80,7 +79,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
|
||||
public LoyaltyCard getCard(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return LoyaltyCard.fromCursor(mContext, mCursor);
|
||||
return LoyaltyCard.toLoyaltyCard(mCursor);
|
||||
}
|
||||
|
||||
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
@@ -88,8 +87,8 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
boolean showDivider = false;
|
||||
inputHolder.mDivider.setVisibility(View.GONE);
|
||||
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.fromCursor(mContext, inputCursor);
|
||||
Bitmap icon = loyaltyCard.getImageThumbnail(mContext);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
|
||||
Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon);
|
||||
|
||||
if (mLoyaltyCardListDisplayOptions.showingNameBelowThumbnail() && icon != null) {
|
||||
showDivider = true;
|
||||
@@ -112,21 +111,22 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
}
|
||||
|
||||
if (mLoyaltyCardListDisplayOptions.showingValidity() && loyaltyCard.validFrom != null) {
|
||||
inputHolder.setExtraField(inputHolder.mValidFromField, DateFormat.getDateInstance(DateFormat.MEDIUM).format(loyaltyCard.validFrom), Utils.isNotYetValid(loyaltyCard.validFrom) ? Color.RED : null, showDivider);
|
||||
inputHolder.setExtraField(inputHolder.mValidFromField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.validFrom), Utils.isNotYetValid(loyaltyCard.validFrom) ? Color.RED : null, showDivider);
|
||||
} else {
|
||||
inputHolder.setExtraField(inputHolder.mValidFromField, null, null, false);
|
||||
}
|
||||
|
||||
if (mLoyaltyCardListDisplayOptions.showingValidity() && loyaltyCard.expiry != null) {
|
||||
inputHolder.setExtraField(inputHolder.mExpiryField, DateFormat.getDateInstance(DateFormat.MEDIUM).format(loyaltyCard.expiry), Utils.hasExpired(loyaltyCard.expiry) ? Color.RED : null, showDivider);
|
||||
inputHolder.setExtraField(inputHolder.mExpiryField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry), Utils.hasExpired(loyaltyCard.expiry) ? Color.RED : null, showDivider);
|
||||
} else {
|
||||
inputHolder.setExtraField(inputHolder.mExpiryField, null, null, false);
|
||||
}
|
||||
|
||||
inputHolder.mCardIcon.setContentDescription(loyaltyCard.store);
|
||||
Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText, new Settings(mContext).getPreferredColumnCount());
|
||||
Utils.setIconOrTextWithBackground(mContext, loyaltyCard, icon, inputHolder.mCardIcon, inputHolder.mCardText);
|
||||
inputHolder.setIconBackgroundColor(Utils.getHeaderColor(mContext, loyaltyCard));
|
||||
|
||||
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0);
|
||||
inputHolder.toggleCardStateIcon(loyaltyCard.starStatus != 0, loyaltyCard.archiveStatus != 0, itemSelected(inputCursor.getPosition()));
|
||||
|
||||
inputHolder.itemView.setActivated(mSelectedItems.get(inputCursor.getPosition(), false));
|
||||
applyIconAnimation(inputHolder, inputCursor.getPosition());
|
||||
@@ -193,7 +193,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
int i;
|
||||
for (i = 0; i < mSelectedItems.size(); i++) {
|
||||
mCursor.moveToPosition(mSelectedItems.keyAt(i));
|
||||
result.add(LoyaltyCard.fromCursor(mContext, mCursor));
|
||||
result.add(LoyaltyCard.toLoyaltyCard(mCursor));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -212,11 +212,13 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
public class LoyaltyCardListItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public TextView mCardText, mStoreField, mNoteField, mBalanceField, mValidFromField, mExpiryField;
|
||||
public ImageView mCardIcon, mTickIcon;
|
||||
public ImageView mCardIcon, mStarBackground, mStarBorder, mTickIcon, mArchivedBackground;
|
||||
public MaterialCardView mRow;
|
||||
public ConstraintLayout mStar, mArchived;
|
||||
public View mDivider;
|
||||
|
||||
private int mIconBackgroundColor;
|
||||
|
||||
protected LoyaltyCardListItemViewHolder(LoyaltyCardLayoutBinding loyaltyCardLayoutBinding, CardAdapterListener inputListener) {
|
||||
super(loyaltyCardLayoutBinding.getRoot());
|
||||
View inputView = loyaltyCardLayoutBinding.getRoot();
|
||||
@@ -230,7 +232,10 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
mCardIcon = loyaltyCardLayoutBinding.thumbnail;
|
||||
mCardText = loyaltyCardLayoutBinding.thumbnailText;
|
||||
mStar = loyaltyCardLayoutBinding.star;
|
||||
mStarBackground = loyaltyCardLayoutBinding.starBackground;
|
||||
mStarBorder = loyaltyCardLayoutBinding.starBorder;
|
||||
mArchived = loyaltyCardLayoutBinding.archivedIcon;
|
||||
mArchivedBackground = loyaltyCardLayoutBinding.archiveBackground;
|
||||
mTickIcon = loyaltyCardLayoutBinding.selectedThumbnail;
|
||||
inputView.setOnLongClickListener(view -> {
|
||||
inputListener.onRowClicked(getAdapterPosition());
|
||||
@@ -292,7 +297,31 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
mNoteField.requestLayout();
|
||||
}
|
||||
|
||||
public void toggleCardStateIcon(boolean enableStar, boolean enableArchive) {
|
||||
public void toggleCardStateIcon(boolean enableStar, boolean enableArchive, boolean colorByTheme) {
|
||||
/* the below code does not work in android 5! hence the change of drawable instead
|
||||
boolean needDarkForeground = Utils.needsDarkForeground(mIconBackgroundColor);
|
||||
Drawable borderDrawable = mStarBorder.getDrawable().mutate();
|
||||
Drawable backgroundDrawable = mStarBackground.getDrawable().mutate();
|
||||
DrawableCompat.setTint(borderDrawable, needsDarkForeground ? Color.BLACK : Color.WHITE);
|
||||
DrawableCompat.setTint(backgroundDrawable, needsDarkForeground ? Color.BLACK : Color.WHITE);
|
||||
mStarBorder.setImageDrawable(borderDrawable);
|
||||
mStarBackground.setImageDrawable(backgroundDrawable);
|
||||
*/
|
||||
boolean dark = Utils.needsDarkForeground(mIconBackgroundColor);
|
||||
if (colorByTheme) {
|
||||
dark = !mDarkModeEnabled;
|
||||
}
|
||||
|
||||
if (dark) {
|
||||
mStarBorder.setImageResource(R.drawable.ic_unstarred_white);
|
||||
mStarBackground.setImageResource(R.drawable.ic_starred_black);
|
||||
mArchivedBackground.setImageResource(R.drawable.ic_baseline_archive_24_black);
|
||||
} else {
|
||||
mStarBorder.setImageResource(R.drawable.ic_unstarred_black);
|
||||
mStarBackground.setImageResource(R.drawable.ic_starred_white);
|
||||
mArchivedBackground.setImageResource(R.drawable.ic_baseline_archive_24);
|
||||
}
|
||||
|
||||
if (enableStar) {
|
||||
mStar.setVisibility(View.VISIBLE);
|
||||
} else{
|
||||
@@ -304,6 +333,22 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
|
||||
} else{
|
||||
mArchived.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mStarBorder.invalidate();
|
||||
mStarBackground.invalidate();
|
||||
mArchivedBackground.invalidate();
|
||||
|
||||
}
|
||||
|
||||
public void setIconBackgroundColor(int color) {
|
||||
mIconBackgroundColor = color;
|
||||
mCardIcon.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public int dpToPx(int dp, Context mContext) {
|
||||
Resources r = mContext.getResources();
|
||||
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
||||
return px;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.ColorStateList;
|
||||
@@ -19,7 +20,6 @@ import android.text.method.DigitsKeyListener;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -60,6 +60,7 @@ import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
@@ -98,35 +99,9 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
static final String STATE_IMAGEINDEX = "imageIndex";
|
||||
static final String STATE_FULLSCREEN = "isFullscreen";
|
||||
|
||||
static final String BUNDLE_ID = "id";
|
||||
static final String BUNDLE_CARDLIST = "cardList";
|
||||
static final String BUNDLE_TRANSITION_RIGHT = "transition_right";
|
||||
|
||||
final private TaskHandler mTasks = new TaskHandler();
|
||||
Runnable barcodeImageGenerationFinishedCallback;
|
||||
|
||||
private long initTime = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (settings.useVolumeKeysForNavigation()) {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
// Navigate to the previous card
|
||||
if (initTime < (System.currentTimeMillis() - 1000)) {
|
||||
prevNextCard(false);
|
||||
}
|
||||
return true;
|
||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
// Navigate to the next card
|
||||
if (initTime < (System.currentTimeMillis() - 1000)) {
|
||||
prevNextCard(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
public void onMainImageTap() {
|
||||
// If we're in fullscreen, leave fullscreen
|
||||
if (isFullscreen) {
|
||||
@@ -208,8 +183,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
loyaltyCardId = b != null ? b.getInt(BUNDLE_ID) : 0;
|
||||
cardList = b != null ? b.getIntegerArrayList(BUNDLE_CARDLIST) : null;
|
||||
loyaltyCardId = b != null ? b.getInt("id") : 0;
|
||||
cardList = b != null ? b.getIntegerArrayList("cardList") : null;
|
||||
Log.d(TAG, "View activity: id=" + loyaltyCardId);
|
||||
}
|
||||
|
||||
@@ -235,7 +210,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
return;
|
||||
}
|
||||
|
||||
int transitionRight = incomingIntentExtras.getInt(BUNDLE_TRANSITION_RIGHT, -1);
|
||||
int transitionRight = incomingIntentExtras.getInt("transition_right", -1);
|
||||
if (transitionRight == 1) {
|
||||
// right side transition
|
||||
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
|
||||
@@ -329,7 +304,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
binding.bottomAppBarUpdateBalanceButton.setOnClickListener(view -> showBalanceUpdateDialog());
|
||||
|
||||
binding.iconContainer.setOnClickListener(view -> {
|
||||
if (loyaltyCard.getImageThumbnail(this) != null) {
|
||||
if (Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.icon) != null) {
|
||||
openImageInGallery(ImageType.ICON);
|
||||
} else {
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.icon_header_click_text, Toast.LENGTH_LONG).show();
|
||||
@@ -445,11 +420,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
private void showBalanceUpdateDialog() {
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this);
|
||||
|
||||
// Header
|
||||
builder.setTitle(R.string.updateBalanceTitle);
|
||||
|
||||
// Layout
|
||||
FrameLayout container = new FrameLayout(this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
@@ -467,91 +438,61 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
currentTextview.setText(getString(R.string.currentBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
layout.addView(currentTextview);
|
||||
|
||||
TextView updateTextView = new TextView(this);
|
||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
layout.addView(updateTextView);
|
||||
|
||||
final TextInputEditText input = new TextInputEditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
Context dialogContext = this;
|
||||
input.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
input.setKeyListener(DigitsKeyListener.getInstance("0123456789,."));
|
||||
input.setHint(R.string.updateBalanceHint);
|
||||
input.addTextChangedListener(new SimpleTextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
BigDecimal newBalance;
|
||||
try {
|
||||
newBalance = calculateNewBalance(loyaltyCard.balance, loyaltyCard.balanceType, s.toString());
|
||||
} catch (ParseException e) {
|
||||
input.setTag(null);
|
||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, loyaltyCard.balance, loyaltyCard.balanceType)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Save new balance into this element
|
||||
input.setTag(newBalance);
|
||||
updateTextView.setText(getString(R.string.newBalanceSentence, Utils.formatBalance(dialogContext, newBalance, loyaltyCard.balanceType)));
|
||||
}
|
||||
});
|
||||
layout.addView(input);
|
||||
layout.setLayoutParams(params);
|
||||
container.addView(layout);
|
||||
|
||||
// Set layout
|
||||
builder.setView(container);
|
||||
|
||||
// Buttons
|
||||
builder.setPositiveButton(R.string.spend, (dialogInterface, i) -> {
|
||||
// Calculate and update balance
|
||||
try {
|
||||
BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType);
|
||||
BigDecimal newBalance = loyaltyCard.balance.subtract(balanceChange).max(new BigDecimal(0));
|
||||
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
||||
} catch (ParseException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show();
|
||||
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||
// Grab calculated balance from input field
|
||||
BigDecimal newBalance = (BigDecimal) input.getTag();
|
||||
if (newBalance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload state
|
||||
// Actually update balance
|
||||
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
||||
// Reload UI
|
||||
this.onResume();
|
||||
|
||||
// Show new balance
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show();
|
||||
});
|
||||
builder.setNegativeButton(R.string.receive, (dialogInterface, i) -> {
|
||||
// Calculate and update balance
|
||||
try {
|
||||
BigDecimal balanceChange = Utils.parseBalance(input.getText().toString(), loyaltyCard.balanceType);
|
||||
BigDecimal newBalance = loyaltyCard.balance.add(balanceChange);
|
||||
DBHelper.updateLoyaltyCardBalance(database, loyaltyCardId, newBalance);
|
||||
} catch (ParseException e) {
|
||||
Toast.makeText(getApplicationContext(), R.string.amountParsingFailed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
// Reload state
|
||||
this.onResume();
|
||||
|
||||
// Show new balance
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.newBalanceSentence, Utils.formatBalance(this, loyaltyCard.balance, loyaltyCard.balanceType)), Toast.LENGTH_LONG).show();
|
||||
});
|
||||
builder.setNeutralButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
// Now that the dialog exists, we can bind something that affects the buttons
|
||||
input.addTextChangedListener(new SimpleTextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
BigDecimal balanceChange;
|
||||
|
||||
try {
|
||||
balanceChange = Utils.parseBalance(s.toString(), loyaltyCard.balanceType);
|
||||
} catch (ParseException e) {
|
||||
input.setError(getString(R.string.amountParsingFailed));
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
input.setError(null);
|
||||
if (balanceChange.equals(new BigDecimal(0))) {
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||
} else {
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
||||
// Disable buttons (must be done **after** dialog is shown to prevent crash
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false);
|
||||
|
||||
// Set focus on input field
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
input.requestFocus();
|
||||
}
|
||||
|
||||
private BigDecimal calculateNewBalance(BigDecimal currentBalance, Currency currency, String unparsedSubtraction) throws ParseException {
|
||||
BigDecimal subtraction = Utils.parseBalance(unparsedSubtraction, currency);
|
||||
return currentBalance.subtract(subtraction).max(new BigDecimal(0));
|
||||
}
|
||||
|
||||
private void setBottomAppBarButtonState() {
|
||||
if (!loyaltyCard.note.isEmpty() || !loyaltyCardGroups.isEmpty() || hasBalance(loyaltyCard) || loyaltyCard.validFrom != null || loyaltyCard.expiry != null) {
|
||||
binding.bottomAppBarInfoButton.setVisibility(View.VISIBLE);
|
||||
@@ -599,8 +540,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
// Restart activity with new card id and index
|
||||
Intent intent = getIntent();
|
||||
Bundle b = intent.getExtras();
|
||||
b.putInt(BUNDLE_ID, loyaltyCardId);
|
||||
b.putInt(BUNDLE_TRANSITION_RIGHT, transitionRight ? 1 : 0);
|
||||
b.putInt("id", loyaltyCardId);
|
||||
b.putInt("transition_right", transitionRight ? 1 : 0);
|
||||
intent.putExtras(b);
|
||||
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
@@ -660,7 +601,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
window.setAttributes(attributes);
|
||||
}
|
||||
|
||||
loyaltyCard = DBHelper.getLoyaltyCard(this, database, loyaltyCardId);
|
||||
loyaltyCard = DBHelper.getLoyaltyCard(database, loyaltyCardId);
|
||||
if (loyaltyCard == null) {
|
||||
Log.w(TAG, "Could not lookup loyalty card " + loyaltyCardId);
|
||||
Toast.makeText(this, R.string.noCardExistsError, Toast.LENGTH_LONG).show();
|
||||
@@ -678,15 +619,10 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
cardIdString = loyaltyCard.cardId;
|
||||
barcodeIdString = loyaltyCard.barcodeId;
|
||||
|
||||
binding.mainImageDescription.setText(loyaltyCard.cardId);
|
||||
binding.cardIdView.setText(loyaltyCard.cardId);
|
||||
|
||||
// Display full text on click in case it doesn't fit in a single line
|
||||
binding.mainImageDescription.setOnClickListener(v -> {
|
||||
if (mainImageIndex != 0) {
|
||||
// Don't show cardId dialog, we're displaying something else
|
||||
return;
|
||||
}
|
||||
|
||||
binding.cardIdView.setOnClickListener(v -> {
|
||||
TextView cardIdView = new TextView(LoyaltyCardViewActivity.this);
|
||||
cardIdView.setText(loyaltyCard.cardId);
|
||||
cardIdView.setTextIsSelectable(true);
|
||||
@@ -719,8 +655,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
editButtonIcon.setTint(Utils.needsDarkForeground(complementaryColor) ? Color.BLACK : Color.WHITE);
|
||||
binding.fabEdit.setImageDrawable(editButtonIcon);
|
||||
|
||||
Bitmap icon = loyaltyCard.getImageThumbnail(this);
|
||||
Utils.setIconOrTextWithBackground(this, loyaltyCard, icon, binding.iconImage, binding.iconText, 1);
|
||||
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);
|
||||
@@ -748,12 +684,12 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
imageTypes.add(ImageType.BARCODE);
|
||||
}
|
||||
|
||||
frontImageBitmap = loyaltyCard.getImageFront(this);
|
||||
frontImageBitmap = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.front);
|
||||
if (frontImageBitmap != null) {
|
||||
imageTypes.add(ImageType.IMAGE_FRONT);
|
||||
}
|
||||
|
||||
backImageBitmap = loyaltyCard.getImageBack(this);
|
||||
backImageBitmap = Utils.retrieveCardImage(this, loyaltyCard.id, ImageLocationType.back);
|
||||
if (backImageBitmap != null) {
|
||||
imageTypes.add(ImageType.IMAGE_BACK);
|
||||
}
|
||||
@@ -765,8 +701,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
DBHelper.updateLoyaltyCardLastUsed(database, loyaltyCard.id);
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
ShortcutHelper.updateShortcuts(this, loyaltyCard);
|
||||
}
|
||||
|
||||
private void setStateBasedOnImageTypes() {
|
||||
@@ -866,8 +800,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(database, loyaltyCardId, 1);
|
||||
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
|
||||
|
||||
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||
|
||||
// Re-init loyaltyCard with new data from DB
|
||||
onResume();
|
||||
invalidateOptionsMenu();
|
||||
@@ -959,9 +891,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
if (imageTypes.isEmpty()) {
|
||||
barcodeRenderTarget.setVisibility(View.GONE);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
binding.mainImageDescription.setTextColor(MaterialColors.getColor(binding.mainImageDescription, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
|
||||
binding.mainImageDescription.setText(loyaltyCard.cardId);
|
||||
binding.cardIdView.setTextColor(MaterialColors.getColor(binding.cardIdView, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -970,7 +900,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
if (wantedImageType == ImageType.BARCODE) {
|
||||
barcodeRenderTarget.setBackgroundColor(Color.WHITE);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.WHITE);
|
||||
binding.mainImageDescription.setTextColor(getResources().getColor(R.color.md_theme_light_onSurfaceVariant));
|
||||
binding.cardIdView.setTextColor(getResources().getColor(R.color.md_theme_light_onSurfaceVariant));
|
||||
|
||||
if (waitForResize) {
|
||||
redrawBarcodeAfterResize(!isFullscreen);
|
||||
@@ -978,23 +908,18 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
drawBarcode(!isFullscreen);
|
||||
}
|
||||
|
||||
binding.mainImageDescription.setText(loyaltyCard.cardId);
|
||||
barcodeRenderTarget.setContentDescription(getString(R.string.barcodeImageDescriptionWithType, format.prettyName()));
|
||||
} else if (wantedImageType == ImageType.IMAGE_FRONT) {
|
||||
barcodeRenderTarget.setImageBitmap(frontImageBitmap);
|
||||
barcodeRenderTarget.setBackgroundColor(Color.TRANSPARENT);
|
||||
binding.mainCardView.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
binding.mainImageDescription.setTextColor(MaterialColors.getColor(binding.mainImageDescription, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
|
||||
binding.mainImageDescription.setText(getString(R.string.frontImageDescription));
|
||||
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.mainImageDescription.setTextColor(MaterialColors.getColor(binding.mainImageDescription, com.google.android.material.R.attr.colorOnSurfaceVariant));
|
||||
|
||||
binding.mainImageDescription.setText(getString(R.string.backImageDescription));
|
||||
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);
|
||||
|
||||
@@ -7,8 +7,8 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.CursorIndexOutOfBoundsException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
@@ -23,30 +23,26 @@ import android.widget.Toast;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import protect.card_locker.databinding.ContentMainBinding;
|
||||
import protect.card_locker.databinding.MainActivityBinding;
|
||||
import protect.card_locker.databinding.SortingOptionBinding;
|
||||
import protect.card_locker.preferences.Settings;
|
||||
import protect.card_locker.preferences.SettingsActivity;
|
||||
|
||||
public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
|
||||
@@ -56,7 +52,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
public static final String RESTART_ACTIVITY_INTENT = "restart_activity_intent";
|
||||
|
||||
private static final int MEDIUM_SCALE_FACTOR_DIP = 460;
|
||||
static final String STATE_SEARCH_QUERY = "SEARCH_QUERY";
|
||||
|
||||
private SQLiteDatabase mDatabase;
|
||||
private LoyaltyCardCursorAdapter mAdapter;
|
||||
@@ -64,8 +59,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
private SearchView mSearchView;
|
||||
private int mLoyaltyCardCount = 0;
|
||||
protected String mFilter = "";
|
||||
private String currentQuery = "";
|
||||
private String finalQuery = "";
|
||||
protected Object mGroup = null;
|
||||
protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
|
||||
protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
|
||||
@@ -75,7 +68,9 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
private View mNoMatchingCardsText;
|
||||
private View mNoGroupCardsText;
|
||||
private TabLayout groupsTabLayout;
|
||||
|
||||
private Runnable mUpdateLoyaltyCardListRunnable;
|
||||
|
||||
private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
|
||||
private ActivityResultLauncher<Intent> mSettingsLauncher;
|
||||
|
||||
@@ -155,7 +150,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Archiving card: " + loyaltyCard.id);
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 1);
|
||||
ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
|
||||
updateLoyaltyCardList(false);
|
||||
inputMode.finish();
|
||||
invalidateOptionsMenu();
|
||||
@@ -200,33 +194,10 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle inputSavedInstanceState) {
|
||||
extractIntentFields(getIntent());
|
||||
SplashScreen.installSplashScreen(this);
|
||||
super.onCreate(inputSavedInstanceState);
|
||||
|
||||
// Delete old cache files
|
||||
// These could be temporary images for the cropper, temporary images in LoyaltyCard toBundle/writeParcel/ etc.
|
||||
new Thread(() -> {
|
||||
long twentyFourHoursAgo = System.currentTimeMillis() - (1000 * 60 * 60 * 24);
|
||||
|
||||
File[] tempFiles = getCacheDir().listFiles();
|
||||
|
||||
if (tempFiles == null) {
|
||||
Log.e(TAG, "getCacheDir().listFiles() somehow returned null, this should never happen... Skipping cache cleanup...");
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : tempFiles) {
|
||||
if (file.lastModified() < twentyFourHoursAgo) {
|
||||
if (!file.delete()) {
|
||||
Log.w(TAG, "Failed to delete cache file " + file.getPath());
|
||||
}
|
||||
};
|
||||
}
|
||||
}).start();
|
||||
|
||||
// We should extract the share intent after we called the super.onCreate as it may need to spawn a dialog window and the app needs to be initialized to not crash
|
||||
extractIntentFields(getIntent());
|
||||
|
||||
binding = MainActivityBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
setSupportActionBar(binding.toolbar);
|
||||
@@ -275,15 +246,52 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
mCardList.setAdapter(mAdapter);
|
||||
registerForContextMenu(mCardList);
|
||||
|
||||
mGroup = null;
|
||||
updateLoyaltyCardList(true);
|
||||
|
||||
/*
|
||||
* This was added for Huawei, but Huawei is just too much of a fucking pain.
|
||||
* Just leaving this commented out if needed for the future idk
|
||||
* https://twitter.com/SylvieLorxu/status/1379437902741012483
|
||||
*
|
||||
|
||||
// Show privacy policy on first run
|
||||
SharedPreferences privacyPolicyShownPref = getApplicationContext().getSharedPreferences(
|
||||
getString(R.string.sharedpreference_privacy_policy_shown),
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
|
||||
if (privacyPolicyShownPref.getInt(getString(R.string.sharedpreference_privacy_policy_shown), 0) == 0) {
|
||||
SharedPreferences.Editor privacyPolicyShownPrefEditor = privacyPolicyShownPref.edit();
|
||||
privacyPolicyShownPrefEditor.putInt(getString(R.string.sharedpreference_privacy_policy_shown), 1);
|
||||
privacyPolicyShownPrefEditor.apply();
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.privacy_policy)
|
||||
.setMessage(R.string.privacy_policy_popup_text)
|
||||
.setPositiveButton(R.string.accept, null)
|
||||
.setNegativeButton(R.string.privacy_policy, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
openPrivacyPolicy();
|
||||
}
|
||||
})
|
||||
.setIcon(android.R.drawable.ic_dialog_info)
|
||||
.show();
|
||||
}
|
||||
*/
|
||||
|
||||
mBarcodeScannerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
// Exit early if the user cancelled the scan (pressed back/home)
|
||||
if (result.getResultCode() != RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent editIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
editIntent.putExtras(result.getData().getExtras());
|
||||
startActivity(editIntent);
|
||||
Intent intent = result.getData();
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);
|
||||
|
||||
Bundle inputBundle = intent.getExtras();
|
||||
String group = inputBundle != null ? inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
|
||||
processBarcodeValues(barcodeValues, group);
|
||||
});
|
||||
|
||||
mSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
@@ -319,6 +327,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
if (mSearchView != null && !mSearchView.isIconified()) {
|
||||
mFilter = mSearchView.getQuery().toString();
|
||||
}
|
||||
|
||||
// Start of active tab logic
|
||||
updateTabGroups(groupsTabLayout);
|
||||
|
||||
@@ -376,12 +385,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
mBarcodeScannerLauncher.launch(intent);
|
||||
});
|
||||
addButton.bringToFront();
|
||||
|
||||
var layoutManager = (GridLayoutManager) mCardList.getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
var settings = new Settings(this);
|
||||
layoutManager.setSpanCount(settings.getPreferredColumnCount());
|
||||
}
|
||||
}
|
||||
|
||||
private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
|
||||
@@ -443,83 +446,64 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
}
|
||||
}
|
||||
|
||||
private void processParseResultList(List<ParseResult> parseResultList, String group, boolean closeAppOnNoBarcode) {
|
||||
if (parseResultList.isEmpty()) {
|
||||
throw new IllegalArgumentException("parseResultList may not be empty");
|
||||
private void processBarcodeValues(BarcodeValues barcodeValues, String group) {
|
||||
if (barcodeValues.isEmpty()) {
|
||||
throw new IllegalArgumentException("barcodesValues may not be empty");
|
||||
}
|
||||
|
||||
Utils.makeUserChooseParseResultFromList(MainActivity.this, parseResultList, new ParseResultListDisambiguatorCallback() {
|
||||
@Override
|
||||
public void onUserChoseParseResult(ParseResult parseResult) {
|
||||
Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle bundle = parseResult.toLoyaltyCardBundle(MainActivity.this);
|
||||
if (group != null) {
|
||||
bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, group);
|
||||
}
|
||||
intent.putExtras(bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserDismissedSelector() {
|
||||
if (closeAppOnNoBarcode) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
|
||||
Bundle newBundle = new Bundle();
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
|
||||
if (group != null) {
|
||||
newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, group);
|
||||
}
|
||||
newIntent.putExtras(newBundle);
|
||||
startActivity(newIntent);
|
||||
}
|
||||
|
||||
private void onSharedIntent(Intent intent) {
|
||||
String receivedAction = intent.getAction();
|
||||
String receivedType = intent.getType();
|
||||
|
||||
if (receivedAction == null || receivedType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ParseResult> parseResultList;
|
||||
|
||||
// Check for shared text
|
||||
if (receivedAction.equals(Intent.ACTION_SEND) && receivedType.equals("text/plain")) {
|
||||
LoyaltyCard loyaltyCard = new LoyaltyCard();
|
||||
loyaltyCard.setCardId(intent.getStringExtra(Intent.EXTRA_TEXT));
|
||||
parseResultList = Collections.singletonList(new ParseResult(ParseResultType.BARCODE_ONLY, loyaltyCard));
|
||||
} else {
|
||||
// Parse whatever file was sent, regardless of opening or sharing
|
||||
Uri data;
|
||||
if (receivedAction.equals(Intent.ACTION_VIEW)) {
|
||||
data = intent.getData();
|
||||
} else if (receivedAction.equals(Intent.ACTION_SEND)) {
|
||||
data = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Log.e(TAG, "Wrong action type to parse intent");
|
||||
return;
|
||||
}
|
||||
|
||||
if (receivedType.startsWith("image/")) {
|
||||
parseResultList = Utils.retrieveBarcodesFromImage(this, data);
|
||||
} else if (receivedType.equals("application/pdf")) {
|
||||
parseResultList = Utils.retrieveBarcodesFromPdf(this, data);
|
||||
} else if (Arrays.asList("application/vnd.apple.pkpass", "application/vnd-com.apple.pkpass").contains(receivedType)) {
|
||||
parseResultList = Utils.retrieveBarcodesFromPkPass(this, data);
|
||||
} else if (receivedType.equals("application/vnd.espass-espass")) {
|
||||
// FIXME: espass is not pkpass
|
||||
// However, several users stated in https://github.com/CatimaLoyalty/Android/issues/2197 that the formats are extremely similar to the point they could rename an .espass file to .pkpass and have it imported
|
||||
// So it makes sense to "unofficially" treat it as a PKPASS for now, even though not completely correct
|
||||
parseResultList = Utils.retrieveBarcodesFromPkPass(this, data);
|
||||
} else {
|
||||
// Check if an image was shared to us
|
||||
if (Intent.ACTION_SEND.equals(receivedAction)) {
|
||||
if (!receivedType.startsWith("image/")) {
|
||||
Log.e(TAG, "Wrong mime-type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Give up if we should parse but there is nothing to parse
|
||||
if (parseResultList == null || parseResultList.isEmpty()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
BarcodeValues barcodeValues;
|
||||
Bitmap bitmap;
|
||||
|
||||
processParseResultList(parseResultList, null, true);
|
||||
Uri data = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (data == null) {
|
||||
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
bitmap = Utils.retrieveImageFromUri(this, data);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting data from image file");
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
barcodeValues = Utils.getBarcodeFromBitmap(bitmap);
|
||||
|
||||
if (barcodeValues.isEmpty()) {
|
||||
Log.i(TAG, "No barcode found in image file");
|
||||
Toast.makeText(this, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
processBarcodeValues(barcodeValues, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
@@ -553,24 +537,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
// Saving currentQuery to finalQuery for user, this will be used to restore search history, happens when user clicks a card from list
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
finalQuery = currentQuery;
|
||||
// Putting the query also into outState for later use in onRestoreInstanceState when rotating screen
|
||||
if (mSearchView != null) {
|
||||
outState.putString(STATE_SEARCH_QUERY, finalQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// Restoring instance state when rotation of screen happens with the goal to restore search query for user
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
finalQuery = savedInstanceState.getString(STATE_SEARCH_QUERY, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu inputMenu) {
|
||||
getMenuInflater().inflate(R.menu.main_menu, inputMenu);
|
||||
@@ -579,42 +545,15 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
|
||||
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
if (searchManager != null) {
|
||||
MenuItem searchMenuItem = inputMenu.findItem(R.id.action_search);
|
||||
mSearchView = (SearchView) searchMenuItem.getActionView();
|
||||
mSearchView = (SearchView) inputMenu.findItem(R.id.action_search).getActionView();
|
||||
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
|
||||
mSearchView.setSubmitButtonEnabled(false);
|
||||
|
||||
mSearchView.setOnCloseListener(() -> {
|
||||
invalidateOptionsMenu();
|
||||
return false;
|
||||
});
|
||||
|
||||
/*
|
||||
* On Android 13 and later, pressing Back while the search view is open hides the keyboard
|
||||
* and collapses the search view at the same time.
|
||||
* This brings back the old behavior on Android 12 and lower: pressing Back once
|
||||
* hides the keyboard, press again while keyboard is hidden to collapse the search view.
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(@NonNull MenuItem item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(@NonNull MenuItem item) {
|
||||
if (mSearchView.hasFocus()) {
|
||||
mSearchView.clearFocus();
|
||||
return false;
|
||||
}
|
||||
currentQuery = "";
|
||||
mFilter = "";
|
||||
updateLoyaltyCardList(false);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
@@ -624,21 +563,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mFilter = newText;
|
||||
// New logic to ensure search history after coming back from picked card - user will see the last search query
|
||||
if (newText.isEmpty()) {
|
||||
if(!finalQuery.isEmpty()){
|
||||
// Setting the query text for user after coming back from picked card from finalQuery
|
||||
mSearchView.setQuery(finalQuery, false);
|
||||
}
|
||||
else if(!currentQuery.isEmpty()){
|
||||
// Else if is needed in case user deletes search - expected behaviour is to show all cards
|
||||
currentQuery = "";
|
||||
mSearchView.setQuery(currentQuery, false);
|
||||
}
|
||||
} else {
|
||||
// Setting search query each time user changes the text in search to temporary variable to be used later in finalQuery String which will be used to restore search history
|
||||
currentQuery = newText;
|
||||
}
|
||||
|
||||
TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
|
||||
mGroup = currentTab != null ? currentTab.getTag() : null;
|
||||
|
||||
@@ -647,14 +572,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Check if we came from a picked card back to search, in that case we want to show the search view with previous search query
|
||||
if(!finalQuery.isEmpty()){
|
||||
// Expand the search view to show the query
|
||||
searchMenuItem.expandActionView();
|
||||
// Setting the query text to empty String due to behaviour of onQueryTextChange after coming back from picked card - onQueryTextChange is called automatically without users interaction
|
||||
finalQuery = "";
|
||||
mSearchView.setQuery(currentQuery, false);
|
||||
}
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(inputMenu);
|
||||
@@ -871,16 +788,18 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
|
||||
Intent intent = new Intent(this, LoyaltyCardViewActivity.class);
|
||||
intent.setAction("");
|
||||
final Bundle b = new Bundle();
|
||||
b.putInt(LoyaltyCardViewActivity.BUNDLE_ID, loyaltyCard.id);
|
||||
b.putInt("id", loyaltyCard.id);
|
||||
|
||||
ArrayList<Integer> cardList = new ArrayList<>();
|
||||
for (int i = 0; i < mAdapter.getItemCount(); i++) {
|
||||
cardList.add(mAdapter.getCard(i).id);
|
||||
}
|
||||
|
||||
b.putIntegerArrayList(LoyaltyCardViewActivity.BUNDLE_CARDLIST, cardList);
|
||||
b.putIntegerArrayList("cardList", cardList);
|
||||
intent.putExtras(b);
|
||||
|
||||
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ManageGroupCursorAdapter extends LoyaltyCardCursorAdapter {
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.fromCursor(mContext, inputCursor);
|
||||
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
|
||||
Boolean overlayValue = mInGroupOverlay.get(loyaltyCard.id);
|
||||
if ((overlayValue != null ? overlayValue : isLoyaltyCardInGroup(loyaltyCard.id))) {
|
||||
mAnimationItemsIndex.put(inputCursor.getPosition(), true);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package protect.card_locker
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
|
||||
class ParseResult(
|
||||
val parseResultType: ParseResultType,
|
||||
val loyaltyCard: LoyaltyCard) {
|
||||
var note: String? = null
|
||||
|
||||
fun toLoyaltyCardBundle(context: Context): Bundle {
|
||||
when (parseResultType) {
|
||||
ParseResultType.FULL -> return loyaltyCard.toBundle(context, listOf())
|
||||
ParseResultType.BARCODE_ONLY -> {
|
||||
val defaultLoyaltyCard = LoyaltyCard()
|
||||
defaultLoyaltyCard.setBarcodeId(null)
|
||||
defaultLoyaltyCard.setBarcodeType(loyaltyCard.barcodeType)
|
||||
defaultLoyaltyCard.setCardId(loyaltyCard.cardId)
|
||||
|
||||
return defaultLoyaltyCard.toBundle(
|
||||
context,
|
||||
listOf(
|
||||
LoyaltyCard.BUNDLE_LOYALTY_CARD_BARCODE_ID,
|
||||
LoyaltyCard.BUNDLE_LOYALTY_CARD_BARCODE_TYPE,
|
||||
LoyaltyCard.BUNDLE_LOYALTY_CARD_CARD_ID
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package protect.card_locker;
|
||||
|
||||
public interface ParseResultListDisambiguatorCallback {
|
||||
void onUserChoseParseResult(ParseResult parseResult);
|
||||
void onUserDismissedSelector();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package protect.card_locker
|
||||
|
||||
enum class ParseResultType {
|
||||
FULL,
|
||||
BARCODE_ONLY
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
package protect.card_locker
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.util.ArrayMap
|
||||
import android.util.Log
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream
|
||||
import net.lingala.zip4j.model.LocalFileHeader
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.math.BigDecimal
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeParseException
|
||||
import java.util.Currency
|
||||
import java.util.Date
|
||||
|
||||
class PkpassParser(context: Context, uri: Uri?) {
|
||||
private var mContext = context
|
||||
|
||||
private var translations: ArrayMap<String, Map<String, String>> = ArrayMap()
|
||||
|
||||
private var passContent: JSONObject? = null
|
||||
|
||||
private var store: String? = null
|
||||
private var note: String? = null
|
||||
private var validFrom: Date? = null
|
||||
private var expiry: Date? = null
|
||||
private val balance: BigDecimal = BigDecimal(0)
|
||||
private val balanceType: Currency? = null
|
||||
private var cardId: String? = null
|
||||
private var barcodeId: String? = null
|
||||
private var barcodeType: CatimaBarcode? = null
|
||||
private var headerColor: Int? = null
|
||||
private val starStatus = 0
|
||||
private val lastUsed: Long = 0
|
||||
private val zoomLevel = DBHelper.DEFAULT_ZOOM_LEVEL
|
||||
private var archiveStatus = 0
|
||||
|
||||
var image: Bitmap? = null
|
||||
private set
|
||||
private var logoSize = 0
|
||||
|
||||
init {
|
||||
if (passContent != null) {
|
||||
throw IllegalStateException("Pkpass instance already initialized!")
|
||||
}
|
||||
|
||||
mContext = context
|
||||
|
||||
Log.i(TAG, "Received Pkpass file")
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Uri did not contain any data")
|
||||
throw IOException(context.getString(R.string.errorReadingFile))
|
||||
}
|
||||
|
||||
try {
|
||||
mContext.contentResolver.openInputStream(uri).use { inputStream ->
|
||||
ZipInputStream(inputStream).use { zipInputStream ->
|
||||
var localFileHeader: LocalFileHeader
|
||||
while ((zipInputStream.nextEntry.also { localFileHeader = it }) != null) {
|
||||
// Ignore directories
|
||||
if (localFileHeader.isDirectory) continue
|
||||
|
||||
// We assume there are three options, as per spec:
|
||||
// language.lproj/pass.strings
|
||||
// file.extension
|
||||
// More directories are ignored
|
||||
val filenameParts = localFileHeader.fileName.split('/')
|
||||
if (filenameParts.size > 2) {
|
||||
continue
|
||||
} else if (filenameParts.size == 2) {
|
||||
// Doesn't seem like a language directory, ignore
|
||||
if (!filenameParts[0].endsWith(".lproj")) continue
|
||||
|
||||
val locale = filenameParts[0].removeSuffix(".lproj")
|
||||
|
||||
translations[locale] = parseLanguageStrings(ZipUtils.read(zipInputStream))
|
||||
}
|
||||
|
||||
// Not a language, parse as normal files
|
||||
when (localFileHeader.fileName) {
|
||||
"logo.png" -> loadImageIfBiggerSize(1, zipInputStream)
|
||||
"logo@2x.png" -> loadImageIfBiggerSize(2, zipInputStream)
|
||||
"logo@3x.png" -> loadImageIfBiggerSize(3, zipInputStream)
|
||||
"pass.json" -> passContent = ZipUtils.readJSON(zipInputStream) // Parse this last, so we're sure we have all language info
|
||||
}
|
||||
}
|
||||
|
||||
checkNotNull(passContent) { "File lacks pass.json" }
|
||||
}
|
||||
}
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw IOException(mContext.getString(R.string.errorReadingFile))
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun listLocales(): List<String> {
|
||||
return translations.keys.toList()
|
||||
}
|
||||
|
||||
fun toLoyaltyCard(locale: String?): LoyaltyCard {
|
||||
parsePassJSON(checkNotNull(passContent) { "Pkpass instance not yet initialized!" }, locale)
|
||||
|
||||
return LoyaltyCard(
|
||||
-1,
|
||||
store,
|
||||
note,
|
||||
validFrom,
|
||||
expiry,
|
||||
balance,
|
||||
balanceType,
|
||||
cardId,
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
starStatus,
|
||||
lastUsed,
|
||||
zoomLevel,
|
||||
archiveStatus,
|
||||
image,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTranslation(string: String, locale: String?): String {
|
||||
if (locale == null) {
|
||||
return string
|
||||
}
|
||||
|
||||
val localeStrings = translations[locale]
|
||||
|
||||
return localeStrings?.get(string) ?: string
|
||||
}
|
||||
|
||||
private fun loadImageIfBiggerSize(fileLogoSize: Int, zipInputStream: ZipInputStream) {
|
||||
if (logoSize < fileLogoSize) {
|
||||
image = ZipUtils.readImage(zipInputStream)
|
||||
logoSize = fileLogoSize
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseColor(color: String): Int? {
|
||||
// First, try formats supported by Android natively
|
||||
try {
|
||||
return Color.parseColor(color)
|
||||
} catch (ignored: IllegalArgumentException) {}
|
||||
|
||||
// If that didn't work, try parsing it as a rbg(0,0,255) value
|
||||
val red: Int;
|
||||
val green: Int;
|
||||
val blue: Int;
|
||||
|
||||
// Parse rgb(0,0,0) string
|
||||
val rgbInfo = Regex("""^rgb\(\s*(?<red>\d+)\s*,\s*(?<green>\d+)\s*,\s*(?<blue>\d+)\s*\)$""").find(color)
|
||||
if (rgbInfo == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Convert to integers
|
||||
try {
|
||||
red = rgbInfo.groups[1]!!.value.toInt()
|
||||
green = rgbInfo.groups[2]!!.value.toInt()
|
||||
blue = rgbInfo.groups[3]!!.value.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Ensure everything is in a valid range as Color.rgb does not do range checks
|
||||
if (red < 0 || red > 255) return null
|
||||
if (green < 0 || green > 255) return null
|
||||
if (blue < 0 || blue > 255) return null
|
||||
|
||||
return Color.rgb(red, green, blue)
|
||||
}
|
||||
|
||||
private fun parseDateTime(dateTime: String): Date {
|
||||
return Date.from(ZonedDateTime.parse(dateTime).toInstant())
|
||||
}
|
||||
|
||||
private fun parseLanguageStrings(data: String): Map<String, String> {
|
||||
val output = ArrayMap<String, String>()
|
||||
|
||||
// Translations look like this:
|
||||
// "key_name" = "Translated value";
|
||||
//
|
||||
// However, "Translated value" may be multiple lines and may contain " (however, it'll be escaped)
|
||||
var translationLine = StringBuilder()
|
||||
|
||||
for (line in data.lines()) {
|
||||
translationLine.append(line)
|
||||
|
||||
// Make sure we don't have a false ending (this is the escaped double quote: \";)
|
||||
if (!line.endsWith("\\\";") and line.endsWith("\";")) {
|
||||
// We reached a translation ending, time to parse it
|
||||
|
||||
// 1. Split into key and value
|
||||
// 2. Remove cruft of each
|
||||
// 3. Clean up escape sequences
|
||||
val keyValue = translationLine.toString().split("=", ignoreCase = false, limit = 2)
|
||||
val key = keyValue[0].trim().removePrefix("\"").removeSuffix("\"")
|
||||
val value = keyValue[1].trim().removePrefix("\"").removeSuffix("\";").replace("\\", "")
|
||||
|
||||
output[key] = value
|
||||
|
||||
translationLine = StringBuilder()
|
||||
} else {
|
||||
translationLine.append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
private fun parsePassJSON(jsonObject: JSONObject, locale: String?) {
|
||||
if (jsonObject.getInt("formatVersion") != 1) {
|
||||
throw IllegalArgumentException(mContext.getString(R.string.unsupportedFile))
|
||||
}
|
||||
|
||||
// Prefer logoText for store, it's generally shorter
|
||||
try {
|
||||
store = jsonObject.getString("logoText")
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
if (store.isNullOrEmpty()) {
|
||||
store = jsonObject.getString("organizationName")
|
||||
}
|
||||
|
||||
val noteText = StringBuilder()
|
||||
noteText.append(getTranslation(jsonObject.getString("description"), locale))
|
||||
|
||||
try {
|
||||
validFrom = parseDateTime(jsonObject.getString("relevantDate"))
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
try {
|
||||
expiry = parseDateTime(jsonObject.getString("expirationDate"))
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
try {
|
||||
headerColor = parseColor(jsonObject.getString("backgroundColor"))
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
var pkPassHasBarcodes = false
|
||||
var validBarcodeFound = false
|
||||
|
||||
// Create a list of possible barcodes
|
||||
val barcodes = ArrayList<JSONObject>()
|
||||
|
||||
// Append the non-deprecated entries
|
||||
try {
|
||||
val foundInBarcodesField = jsonObject.getJSONArray("barcodes")
|
||||
|
||||
for (i in 0 until foundInBarcodesField.length()) {
|
||||
barcodes.add(foundInBarcodesField.getJSONObject(i))
|
||||
}
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
// Append the deprecated entry if it exists
|
||||
try {
|
||||
barcodes.add(jsonObject.getJSONObject("barcode"))
|
||||
} catch (ignored: JSONException) {}
|
||||
|
||||
for (barcode in barcodes) {
|
||||
pkPassHasBarcodes = true
|
||||
|
||||
try {
|
||||
parsePassJSONBarcodeField(barcode)
|
||||
|
||||
validBarcodeFound = true
|
||||
break
|
||||
} catch (ignored: IllegalArgumentException) {}
|
||||
}
|
||||
|
||||
if (pkPassHasBarcodes && !validBarcodeFound) {
|
||||
throw FormatException(mContext.getString(R.string.errorReadingFile))
|
||||
}
|
||||
|
||||
// An used card being "archived" probably is the most sensible way to map "voided"
|
||||
archiveStatus = try {
|
||||
if (jsonObject.getBoolean("voided")) 1 else 0
|
||||
} catch (ignored: JSONException) {
|
||||
0
|
||||
}
|
||||
|
||||
// Append type-specific info to the pass
|
||||
noteText.append("\n\n")
|
||||
|
||||
// Find the relevant pass type and parse it
|
||||
var hasPassData = false
|
||||
for (passType in listOf("boardingPass", "coupon", "eventTicket", "generic")) {
|
||||
try {
|
||||
noteText.append(
|
||||
parsePassJSONPassFields(
|
||||
jsonObject.getJSONObject(passType),
|
||||
locale
|
||||
)
|
||||
)
|
||||
|
||||
hasPassData = true
|
||||
|
||||
break
|
||||
} catch (ignored: JSONException) {}
|
||||
}
|
||||
|
||||
// Failed to parse anything, error out
|
||||
if (!hasPassData) {
|
||||
throw FormatException(mContext.getString(R.string.errorReadingFile))
|
||||
}
|
||||
|
||||
note = noteText.toString()
|
||||
}
|
||||
|
||||
/* Return success or failure */
|
||||
private fun parsePassJSONBarcodeField(barcodeInfo: JSONObject) {
|
||||
val format = barcodeInfo.getString("format")
|
||||
|
||||
// We only need to check these 4 formats as no other options are valid in the PkPass spec
|
||||
barcodeType = when(format) {
|
||||
"PKBarcodeFormatQR" -> CatimaBarcode.fromBarcode(BarcodeFormat.QR_CODE)
|
||||
"PKBarcodeFormatPDF417" -> CatimaBarcode.fromBarcode(BarcodeFormat.PDF_417)
|
||||
"PKBarcodeFormatAztec" -> CatimaBarcode.fromBarcode(BarcodeFormat.AZTEC)
|
||||
"PKBarcodeFormatCode128" -> CatimaBarcode.fromBarcode(BarcodeFormat.CODE_128)
|
||||
else -> throw IllegalArgumentException("No valid barcode type")
|
||||
}
|
||||
|
||||
// FIXME: We probably need to do something with the messageEncoding field
|
||||
try {
|
||||
cardId = barcodeInfo.getString("altText")
|
||||
barcodeId = barcodeInfo.getString("message")
|
||||
} catch (ignored: JSONException) {
|
||||
cardId = barcodeInfo.getString("message")
|
||||
barcodeId = null
|
||||
}
|
||||
|
||||
// Don't set barcodeId if it's the same as cardId
|
||||
if (cardId == barcodeId) {
|
||||
barcodeId = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parsePassJSONPassFields(fieldsParent: JSONObject, locale: String?): String {
|
||||
// These fields contain a lot of info on where we're supposed to display them, but Catima doesn't really have anything for that
|
||||
// So for now, throw them all into the description field in a logical order
|
||||
val noteContents: MutableList<String> = ArrayList()
|
||||
|
||||
// Collect all the groups of fields that exist
|
||||
for (fieldType in listOf("headerFields", "primaryFields", "secondaryFields", "auxiliaryFields", "backFields")) {
|
||||
val content = StringBuilder()
|
||||
|
||||
try {
|
||||
val fieldArray = fieldsParent.getJSONArray(fieldType)
|
||||
for (i in 0 until fieldArray.length()) {
|
||||
val entry = fieldArray.getJSONObject(i)
|
||||
|
||||
content.append(parsePassJSONPassField(entry, locale))
|
||||
|
||||
// If this is not the last part, add spacing on the end
|
||||
if (i < (fieldArray.length() - 1)) {
|
||||
content.append("\n")
|
||||
}
|
||||
}
|
||||
} catch (ignore: JSONException) {
|
||||
} catch (ignore: ParseException) {
|
||||
}
|
||||
|
||||
if (content.isNotEmpty()) {
|
||||
noteContents.add(content.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Merge all field groups together, one paragraph for field group
|
||||
val output = StringBuilder()
|
||||
|
||||
for (i in 0 until noteContents.size) {
|
||||
output.append(noteContents[i])
|
||||
|
||||
// If this is not the last part, add newlines to separate
|
||||
if (i < (noteContents.size - 1)) {
|
||||
output.append("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
return output.toString()
|
||||
}
|
||||
|
||||
private fun parsePassJSONPassField(field: JSONObject, locale: String?): String {
|
||||
// Value may be a localizable string, a date or a number. So let's try to parse it as a date first
|
||||
|
||||
var value = getTranslation(field.getString("value"), locale)
|
||||
try {
|
||||
value = DateFormat.getDateTimeInstance().format(parseDateTime(value))
|
||||
} catch (ignored: DateTimeParseException) {
|
||||
// It's fine if it's not a date
|
||||
}
|
||||
|
||||
// FIXME: Use the Android thing for formatted strings here
|
||||
if (field.has("currencyCode")) {
|
||||
val valueCurrency = Currency.getInstance(field.getString("currencyCode"))
|
||||
|
||||
value = Utils.formatBalance(
|
||||
mContext,
|
||||
Utils.parseBalance(value, valueCurrency),
|
||||
valueCurrency
|
||||
)
|
||||
} else if (field.has("numberStyle")) {
|
||||
if (field.getString("numberStyle") == "PKNumberStylePercent") {
|
||||
// FIXME: Android formatting string
|
||||
value = "${value}%"
|
||||
}
|
||||
}
|
||||
|
||||
val label = getTranslation(field.getString("label"), locale)
|
||||
|
||||
if (label.isNotEmpty()) {
|
||||
return "$label: $value"
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "Catima"
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,6 @@ import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.SimpleAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -44,8 +42,6 @@ import com.journeyapps.barcodescanner.BarcodeResult;
|
||||
import com.journeyapps.barcodescanner.CaptureManager;
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import protect.card_locker.databinding.CustomBarcodeScannerBinding;
|
||||
@@ -66,8 +62,6 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
private static final int COMPAT_SCALE_FACTOR_DIP = 320;
|
||||
|
||||
private static final int PERMISSION_SCAN_ADD_FROM_IMAGE = 100;
|
||||
private static final int PERMISSION_SCAN_ADD_FROM_PDF = 101;
|
||||
private static final int PERMISSION_SCAN_ADD_FROM_PKPASS = 102;
|
||||
|
||||
private CaptureManager capture;
|
||||
private DecoratedBarcodeView barcodeScannerView;
|
||||
@@ -79,16 +73,13 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
private ActivityResultLauncher<Intent> manualAddLauncher;
|
||||
// can't use the pre-made contract because that launches the file manager for image type instead of gallery
|
||||
private ActivityResultLauncher<Intent> photoPickerLauncher;
|
||||
private ActivityResultLauncher<Intent> pdfPickerLauncher;
|
||||
private ActivityResultLauncher<Intent> pkpassPickerLauncher;
|
||||
|
||||
static final String STATE_SCANNER_ACTIVE = "scannerActive";
|
||||
private boolean mScannerActive = true;
|
||||
private boolean mHasError = false;
|
||||
|
||||
private void extractIntentFields(Intent intent) {
|
||||
final Bundle b = intent.getExtras();
|
||||
cardId = b != null ? b.getString(LoyaltyCard.BUNDLE_LOYALTY_CARD_CARD_ID) : null;
|
||||
cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
|
||||
addGroup = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
|
||||
Log.d(TAG, "Scan activity: id=" + cardId);
|
||||
}
|
||||
@@ -108,47 +99,17 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
|
||||
manualAddLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.SELECT_BARCODE_REQUEST, result.getResultCode(), result.getData()));
|
||||
photoPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_IMAGE_FILE, result.getResultCode(), result.getData()));
|
||||
pdfPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_PDF_FILE, result.getResultCode(), result.getData()));
|
||||
pkpassPickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> handleActivityResult(Utils.BARCODE_IMPORT_FROM_PKPASS_FILE, result.getResultCode(), result.getData()));
|
||||
customBarcodeScannerBinding.fabOtherOptions.setOnClickListener(view -> {
|
||||
setScannerActive(false);
|
||||
|
||||
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
|
||||
String[] texts = new String[]{
|
||||
getString(R.string.addWithoutBarcode),
|
||||
getString(R.string.addManually),
|
||||
getString(R.string.addFromImage),
|
||||
getString(R.string.addFromPdfFile),
|
||||
getString(R.string.addFromPkpass)
|
||||
};
|
||||
Object[] icons = new Object[]{
|
||||
R.drawable.baseline_block_24,
|
||||
R.drawable.ic_edit,
|
||||
R.drawable.baseline_image_24,
|
||||
R.drawable.baseline_picture_as_pdf_24,
|
||||
R.drawable.local_activity_24px
|
||||
};
|
||||
String[] columns = new String[]{"text", "icon"};
|
||||
|
||||
for (int i = 0; i < texts.length; i++) {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
map.put(columns[0], texts[i]);
|
||||
map.put(columns[1], icons[i]);
|
||||
list.add(map);
|
||||
}
|
||||
|
||||
ListAdapter adapter = new SimpleAdapter(
|
||||
ScanActivity.this,
|
||||
list,
|
||||
R.layout.alertdialog_row_with_icon,
|
||||
columns,
|
||||
new int[]{R.id.textView, R.id.imageView}
|
||||
);
|
||||
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ScanActivity.this);
|
||||
builder.setTitle(getString(R.string.add_a_card_in_a_different_way));
|
||||
builder.setAdapter(
|
||||
adapter,
|
||||
builder.setItems(
|
||||
new CharSequence[]{
|
||||
getString(R.string.addWithoutBarcode),
|
||||
getString(R.string.addManually),
|
||||
getString(R.string.addFromImage)
|
||||
},
|
||||
(dialogInterface, i) -> {
|
||||
switch (i) {
|
||||
case 0:
|
||||
@@ -160,12 +121,6 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
case 2:
|
||||
addFromImage();
|
||||
break;
|
||||
case 3:
|
||||
addFromPdf();
|
||||
break;
|
||||
case 4:
|
||||
addFromPkPass();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown 'Add a card in a different way' dialog option");
|
||||
}
|
||||
@@ -179,7 +134,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
|
||||
// Even though we do the actual decoding with the barcodeScannerView
|
||||
// CaptureManager needs to be running to show the camera and scanning bar
|
||||
capture = new CatimaCaptureManager(this, barcodeScannerView, this::onCaptureManagerError);
|
||||
capture = new CatimaCaptureManager(this, barcodeScannerView);
|
||||
Intent captureIntent = new Intent();
|
||||
Bundle captureIntentBundle = new Bundle();
|
||||
captureIntentBundle.putBoolean(Intents.Scan.BEEP_ENABLED, false);
|
||||
@@ -189,11 +144,16 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
barcodeScannerView.decodeSingle(new BarcodeCallback() {
|
||||
@Override
|
||||
public void barcodeResult(BarcodeResult result) {
|
||||
LoyaltyCard loyaltyCard = new LoyaltyCard();
|
||||
loyaltyCard.setCardId(result.getText());
|
||||
loyaltyCard.setBarcodeType(CatimaBarcode.fromBarcode(result.getBarcodeFormat()));
|
||||
|
||||
returnResult(new ParseResult(ParseResultType.BARCODE_ONLY, loyaltyCard));
|
||||
Intent scanResult = new Intent();
|
||||
Bundle scanResultBundle = new Bundle();
|
||||
scanResultBundle.putString(BARCODE_CONTENTS, result.getText());
|
||||
scanResultBundle.putString(BARCODE_FORMAT, result.getBarcodeFormat().name());
|
||||
if (addGroup != null) {
|
||||
scanResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
|
||||
}
|
||||
scanResult.putExtras(scanResultBundle);
|
||||
ScanActivity.this.setResult(RESULT_OK, scanResult);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -211,14 +171,9 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
capture.onResume();
|
||||
}
|
||||
|
||||
if (!Utils.deviceHasCamera(this)) {
|
||||
showCameraError(getString(R.string.noCameraFoundGuideText), false);
|
||||
} else if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
showCameraPermissionMissingText();
|
||||
} else {
|
||||
hideCameraError();
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||
showCameraPermissionMissingText(false);
|
||||
}
|
||||
|
||||
scaleScreen();
|
||||
}
|
||||
|
||||
@@ -297,38 +252,30 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
mScannerActive = isActive;
|
||||
}
|
||||
|
||||
private void returnResult(ParseResult parseResult) {
|
||||
Intent result = new Intent();
|
||||
Bundle bundle = parseResult.toLoyaltyCardBundle(ScanActivity.this);
|
||||
private void returnResult(String barcodeContents, String barcodeFormat) {
|
||||
Intent manualResult = new Intent();
|
||||
Bundle manualResultBundle = new Bundle();
|
||||
manualResultBundle.putString(BARCODE_CONTENTS, barcodeContents);
|
||||
manualResultBundle.putString(BARCODE_FORMAT, barcodeFormat);
|
||||
if (addGroup != null) {
|
||||
bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
|
||||
manualResultBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, addGroup);
|
||||
}
|
||||
result.putExtras(bundle);
|
||||
ScanActivity.this.setResult(RESULT_OK, result);
|
||||
manualResult.putExtras(manualResultBundle);
|
||||
ScanActivity.this.setResult(RESULT_OK, manualResult);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
List<ParseResult> parseResultList = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(requestCode, resultCode, intent, this);
|
||||
|
||||
if (parseResultList.isEmpty()) {
|
||||
if (barcodeValues.isEmpty()) {
|
||||
setScannerActive(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.makeUserChooseParseResultFromList(this, parseResultList, new ParseResultListDisambiguatorCallback() {
|
||||
@Override
|
||||
public void onUserChoseParseResult(ParseResult parseResult) {
|
||||
returnResult(parseResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserDismissedSelector() {
|
||||
setScannerActive(true);
|
||||
}
|
||||
});
|
||||
returnResult(barcodeValues.content(), barcodeValues.format());
|
||||
}
|
||||
|
||||
private void addWithoutBarcode() {
|
||||
@@ -368,9 +315,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
|
||||
// Buttons
|
||||
builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
|
||||
LoyaltyCard loyaltyCard = new LoyaltyCard();
|
||||
loyaltyCard.setCardId(input.getText().toString());
|
||||
returnResult(new ParseResult(ParseResultType.BARCODE_ONLY, loyaltyCard));
|
||||
returnResult(input.getText().toString(), "");
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
@@ -405,7 +350,7 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
Intent i = new Intent(getApplicationContext(), BarcodeSelectorActivity.class);
|
||||
if (cardId != null) {
|
||||
final Bundle b = new Bundle();
|
||||
b.putString(LoyaltyCard.BUNDLE_LOYALTY_CARD_CARD_ID, cardId);
|
||||
b.putString("initialCardId", cardId);
|
||||
i.putExtras(b);
|
||||
}
|
||||
manualAddLauncher.launch(i);
|
||||
@@ -419,62 +364,29 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_IMAGE);
|
||||
}
|
||||
|
||||
public void addFromPdf() {
|
||||
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_PDF);
|
||||
}
|
||||
|
||||
public void addFromPkPass() {
|
||||
PermissionUtils.requestStorageReadPermission(this, PERMISSION_SCAN_ADD_FROM_PKPASS);
|
||||
}
|
||||
|
||||
private void addFromImageOrFileAfterPermission(String mimeType, ActivityResultLauncher<Intent> launcher, int chooserText, int errorMessage) {
|
||||
private void addFromImageAfterPermission() {
|
||||
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||
photoPickerIntent.setType(mimeType);
|
||||
photoPickerIntent.setType("image/*");
|
||||
Intent contentIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
contentIntent.setType(mimeType);
|
||||
contentIntent.setType("image/*");
|
||||
|
||||
Intent chooserIntent = Intent.createChooser(photoPickerIntent, getString(chooserText));
|
||||
Intent chooserIntent = Intent.createChooser(photoPickerIntent, getString(R.string.addFromImage));
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { contentIntent });
|
||||
try {
|
||||
launcher.launch(chooserIntent);
|
||||
photoPickerLauncher.launch(chooserIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
setScannerActive(true);
|
||||
Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(getApplicationContext(), R.string.failedLaunchingPhotoPicker, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "No activity found to handle intent", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCaptureManagerError(String errorMessage) {
|
||||
if (mHasError) {
|
||||
// We're already showing an error, ignore this new error
|
||||
return;
|
||||
}
|
||||
|
||||
showCameraError(errorMessage, false);
|
||||
}
|
||||
|
||||
private void showCameraPermissionMissingText() {
|
||||
showCameraError(getString(R.string.noCameraPermissionDirectToSystemSetting), true);
|
||||
}
|
||||
|
||||
private void showCameraError(String message, boolean setOnClick) {
|
||||
customBarcodeScannerBinding.cameraErrorLayout.cameraErrorMessage.setText(message);
|
||||
|
||||
setCameraErrorState(true, setOnClick);
|
||||
}
|
||||
|
||||
private void hideCameraError() {
|
||||
setCameraErrorState(false, false);
|
||||
}
|
||||
|
||||
private void setCameraErrorState(boolean visible, boolean setOnClick) {
|
||||
mHasError = visible;
|
||||
|
||||
customBarcodeScannerBinding.cameraErrorLayout.cameraErrorClickableArea.setOnClickListener(visible && setOnClick ? v -> {
|
||||
private void showCameraPermissionMissingText(boolean show) {
|
||||
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedClickableArea.setOnClickListener(show ? v -> {
|
||||
navigateToSystemPermissionSetting();
|
||||
} : null);
|
||||
customBarcodeScannerBinding.cardInputContainer.setBackgroundColor(visible ? obtainThemeAttribute(com.google.android.material.R.attr.colorSurface) : Color.TRANSPARENT);
|
||||
customBarcodeScannerBinding.cameraErrorLayout.getRoot().setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
customBarcodeScannerBinding.cardInputContainer.setBackgroundColor(show ? obtainThemeAttribute(com.google.android.material.R.attr.colorSurface) : Color.TRANSPARENT);
|
||||
customBarcodeScannerBinding.cameraPermissionDeniedLayout.getRoot().setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void scaleScreen() {
|
||||
@@ -484,8 +396,8 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
float mediumSizePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,MEDIUM_SCALE_FACTOR_DIP,getResources().getDisplayMetrics());
|
||||
boolean shouldScaleSmaller = screenHeight < mediumSizePx;
|
||||
|
||||
customBarcodeScannerBinding.cameraErrorLayout.cameraErrorIcon.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
|
||||
customBarcodeScannerBinding.cameraErrorLayout.cameraErrorTitle.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
|
||||
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedIcon.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
|
||||
customBarcodeScannerBinding.cameraPermissionDeniedLayout.cameraPermissionDeniedTitle.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private int obtainThemeAttribute(int attribute) {
|
||||
@@ -511,20 +423,10 @@ public class ScanActivity extends CatimaAppCompatActivity {
|
||||
boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (requestCode == CaptureManager.getCameraPermissionReqCode()) {
|
||||
showCameraPermissionMissingText(!granted);
|
||||
} else if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE) {
|
||||
if (granted) {
|
||||
hideCameraError();
|
||||
} else {
|
||||
showCameraPermissionMissingText();
|
||||
}
|
||||
} else if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE || requestCode == PERMISSION_SCAN_ADD_FROM_PDF || requestCode == PERMISSION_SCAN_ADD_FROM_PKPASS) {
|
||||
if (granted) {
|
||||
if (requestCode == PERMISSION_SCAN_ADD_FROM_IMAGE) {
|
||||
addFromImageOrFileAfterPermission("image/*", photoPickerLauncher, R.string.addFromImage, R.string.failedLaunchingPhotoPicker);
|
||||
} else if (requestCode == PERMISSION_SCAN_ADD_FROM_PDF) {
|
||||
addFromImageOrFileAfterPermission("application/pdf", pdfPickerLauncher, R.string.addFromPdfFile, R.string.failedLaunchingFileManager);
|
||||
} else {
|
||||
addFromImageOrFileAfterPermission("application/*", pkpassPickerLauncher, R.string.addFromPkpass, R.string.failedLaunchingFileManager);
|
||||
}
|
||||
addFromImageAfterPermission();
|
||||
} else {
|
||||
setScannerActive(true);
|
||||
Toast.makeText(this, R.string.storageReadPermissionRequired, Toast.LENGTH_LONG).show();
|
||||
|
||||
@@ -33,6 +33,7 @@ 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_OVERLAY = Color.argb(127, 0, 0, 0);
|
||||
|
||||
/**
|
||||
* Add a card to the app shortcuts, and maintain a list of the most
|
||||
@@ -42,11 +43,6 @@ class ShortcutHelper {
|
||||
* used card shortcut is discarded.
|
||||
*/
|
||||
static void updateShortcuts(Context context, LoyaltyCard card) {
|
||||
if (card.archiveStatus == 1) {
|
||||
// Don't add archived card to menu
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
||||
|
||||
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
||||
@@ -86,7 +82,7 @@ class ShortcutHelper {
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
ShortcutInfoCompat prevShortcut = list.get(index);
|
||||
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(context, database, Integer.parseInt(prevShortcut.getId()));
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(database, Integer.parseInt(prevShortcut.getId()));
|
||||
|
||||
// skip outdated cards that no longer exist
|
||||
if (loyaltyCard != null) {
|
||||
@@ -112,14 +108,25 @@ class ShortcutHelper {
|
||||
* shortcut exists.
|
||||
*/
|
||||
static void removeShortcut(Context context, int cardId) {
|
||||
ShortcutManagerCompat.removeDynamicShortcuts(context, Collections.singletonList(Integer.toString(cardId)));
|
||||
List<ShortcutInfoCompat> list = ShortcutManagerCompat.getDynamicShortcuts(context);
|
||||
|
||||
String shortcutId = Integer.toString(cardId);
|
||||
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
if (list.get(index).getId().equals(shortcutId)) {
|
||||
list.remove(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, list);
|
||||
}
|
||||
|
||||
static @NotNull
|
||||
Bitmap createAdaptiveBitmap(@NotNull Bitmap in, int paddingColor) {
|
||||
Bitmap ret = Bitmap.createBitmap(ADAPTIVE_BITMAP_SIZE, ADAPTIVE_BITMAP_SIZE, Bitmap.Config.ARGB_8888);
|
||||
Canvas output = new Canvas(ret);
|
||||
output.drawColor(paddingColor);
|
||||
output.drawColor(ColorUtils.compositeColors(PADDING_COLOR_OVERLAY, paddingColor));
|
||||
Bitmap resized = Utils.resizeBitmap(in, ADAPTIVE_BITMAP_IMAGE_SIZE);
|
||||
output.drawBitmap(resized, (ADAPTIVE_BITMAP_SIZE - resized.getWidth()) / 2f, (ADAPTIVE_BITMAP_SIZE - resized.getHeight()) / 2f, null);
|
||||
return ret;
|
||||
@@ -132,14 +139,15 @@ class ShortcutHelper {
|
||||
// one replace it.
|
||||
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt(LoyaltyCardViewActivity.BUNDLE_ID, loyaltyCard.id);
|
||||
bundle.putInt("id", loyaltyCard.id);
|
||||
bundle.putBoolean("view", true);
|
||||
intent.putExtras(bundle);
|
||||
|
||||
Bitmap iconBitmap = loyaltyCard.getImageThumbnail(context);
|
||||
Bitmap iconBitmap = Utils.retrieveCardImage(context, loyaltyCard.id, ImageLocationType.icon);
|
||||
if (iconBitmap == null) {
|
||||
iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
|
||||
} else {
|
||||
iconBitmap = createAdaptiveBitmap(iconBitmap, Utils.needsDarkForeground(Utils.getHeaderColor(context, loyaltyCard)) ? Color.BLACK : Color.WHITE);
|
||||
iconBitmap = createAdaptiveBitmap(iconBitmap, Utils.getHeaderColor(context, loyaltyCard));
|
||||
}
|
||||
|
||||
IconCompat icon = IconCompat.createWithAdaptiveBitmap(iconBitmap);
|
||||
|
||||
@@ -9,16 +9,11 @@ import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.pdf.PdfRenderer;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
@@ -40,12 +35,10 @@ import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.os.LocaleListCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
@@ -53,8 +46,6 @@ import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.RGBLuminanceSource;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.multi.GenericMultipleBarcodeReader;
|
||||
import com.google.zxing.multi.MultipleBarcodeReader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -69,17 +60,13 @@ import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -96,14 +83,12 @@ public class Utils {
|
||||
public static final int SELECT_BARCODE_REQUEST = 2;
|
||||
public static final int BARCODE_SCAN = 3;
|
||||
public static final int BARCODE_IMPORT_FROM_IMAGE_FILE = 4;
|
||||
public static final int BARCODE_IMPORT_FROM_PDF_FILE = 5;
|
||||
public static final int BARCODE_IMPORT_FROM_PKPASS_FILE = 6;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 7;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 8;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_ICON = 9;
|
||||
public static final int CARD_IMAGE_FROM_FILE_FRONT = 10;
|
||||
public static final int CARD_IMAGE_FROM_FILE_BACK = 11;
|
||||
public static final int CARD_IMAGE_FROM_FILE_ICON = 12;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_FRONT = 5;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_BACK = 6;
|
||||
public static final int CARD_IMAGE_FROM_CAMERA_ICON = 7;
|
||||
public static final int CARD_IMAGE_FROM_FILE_FRONT = 8;
|
||||
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)$";
|
||||
|
||||
@@ -146,159 +131,56 @@ public class Utils {
|
||||
return ColorUtils.calculateLuminance(backgroundColor) > LUMINANCE_MIDPOINT;
|
||||
}
|
||||
|
||||
static public List<ParseResult> retrieveBarcodesFromImage(Context context, Uri uri) {
|
||||
Log.i(TAG, "Received image file with possible barcode");
|
||||
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Uri did not contain any data");
|
||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = retrieveImageFromUri(context, uri);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting data from image file");
|
||||
e.printStackTrace();
|
||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<ParseResult> barcodesFromBitmap = getBarcodesFromBitmap(bitmap);
|
||||
|
||||
if (barcodesFromBitmap.isEmpty()) {
|
||||
Log.i(TAG, "No barcode found in image file");
|
||||
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
return barcodesFromBitmap;
|
||||
}
|
||||
|
||||
static public List<ParseResult> retrieveBarcodesFromPkPass(Context context, Uri uri) {
|
||||
Log.i(TAG, "Received Pkpass file with possible barcode");
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Pkpass did not contain any data");
|
||||
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
PkpassParser pkpassParser;
|
||||
try {
|
||||
pkpassParser = new PkpassParser(context, uri);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error reading pkpass file", e);
|
||||
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<String> locales = pkpassParser.listLocales();
|
||||
if (locales.isEmpty()) {
|
||||
return Collections.singletonList(new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(null)));
|
||||
}
|
||||
|
||||
List<ParseResult> parseResultList = new ArrayList<>();
|
||||
for (String locale : locales) {
|
||||
ParseResult parseResult = new ParseResult(ParseResultType.FULL, pkpassParser.toLoyaltyCard(locale));
|
||||
parseResult.setNote(locale);
|
||||
parseResultList.add(parseResult);
|
||||
}
|
||||
|
||||
return parseResultList;
|
||||
}
|
||||
|
||||
static public List<ParseResult> retrieveBarcodesFromPdf(Context context, Uri uri) {
|
||||
Log.i(TAG, "Received PDF file with possible barcode");
|
||||
if (uri == null) {
|
||||
Log.e(TAG, "Uri did not contain any data");
|
||||
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
ParcelFileDescriptor parcelFileDescriptor = null;
|
||||
PdfRenderer renderer = null;
|
||||
List<ParseResult> barcodesFromPdfPages = new ArrayList<>();
|
||||
|
||||
try {
|
||||
parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
if (parcelFileDescriptor != null) {
|
||||
renderer = new PdfRenderer(parcelFileDescriptor);
|
||||
|
||||
// Loop over all pages to find barcodes
|
||||
Bitmap renderedPage;
|
||||
for (int i = 0; i < renderer.getPageCount(); i++) {
|
||||
PdfRenderer.Page page = renderer.openPage(i);
|
||||
renderedPage = Bitmap.createBitmap(page.getWidth(), page.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
// Ensure the page has a background
|
||||
// Fixes some transparent PDF files not being read well
|
||||
Canvas canvas = new Canvas(renderedPage);
|
||||
canvas.drawColor(Color.WHITE);
|
||||
canvas.drawBitmap(renderedPage, 0, 0, null);
|
||||
|
||||
page.render(renderedPage, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
|
||||
page.close();
|
||||
|
||||
List<ParseResult> barcodesFromPage = getBarcodesFromBitmap(renderedPage);
|
||||
for (ParseResult parseResult : barcodesFromPage) {
|
||||
parseResult.setNote(String.format(context.getString(R.string.pageWithNumber), i+1));
|
||||
barcodesFromPdfPages.add(parseResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading PDF file", e);
|
||||
Toast.makeText(context, R.string.errorReadingFile, Toast.LENGTH_LONG).show();
|
||||
} finally {
|
||||
// Resource handling
|
||||
if (renderer != null) {
|
||||
renderer.close();
|
||||
}
|
||||
if (parcelFileDescriptor != null) {
|
||||
try {
|
||||
parcelFileDescriptor.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error closing ParcelFileDescriptor", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (barcodesFromPdfPages.isEmpty()) {
|
||||
Log.i(TAG, "No barcode found in pdf file");
|
||||
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return barcodesFromPdfPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ParseResult based on the result of an activity.
|
||||
* It shows toasts to notify the end-user as needed itself and will return an empty list if the
|
||||
* activity was cancelled or nothing could be found.
|
||||
* Returns the Barcode format and content based on the result of an activity.
|
||||
* It shows toasts to notify the end-user as needed itself and will return an empty
|
||||
* BarcodeValues object if the activity was cancelled or nothing could be found.
|
||||
*
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param intent
|
||||
* @param context
|
||||
* @return List<ParseResult>
|
||||
* @return BarcodeValues
|
||||
*/
|
||||
static public List<ParseResult> parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
|
||||
static public BarcodeValues parseSetBarcodeActivityResult(int requestCode, int resultCode, Intent intent, Context context) {
|
||||
String contents;
|
||||
String format;
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return new ArrayList<>();
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
if (requestCode == Utils.BARCODE_IMPORT_FROM_IMAGE_FILE) {
|
||||
return retrieveBarcodesFromImage(context, intent.getData());
|
||||
}
|
||||
Log.i(TAG, "Received image file with possible barcode");
|
||||
|
||||
if (requestCode == Utils.BARCODE_IMPORT_FROM_PDF_FILE) {
|
||||
return retrieveBarcodesFromPdf(context, intent.getData());
|
||||
}
|
||||
Uri data = intent.getData();
|
||||
if (data == null) {
|
||||
Log.e(TAG, "Intent did not contain any data");
|
||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
if (requestCode == Utils.BARCODE_IMPORT_FROM_PKPASS_FILE) {
|
||||
return retrieveBarcodesFromPkPass(context, intent.getData());
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = retrieveImageFromUri(context, data);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting data from image file");
|
||||
e.printStackTrace();
|
||||
Toast.makeText(context, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
BarcodeValues barcodeFromBitmap = getBarcodeFromBitmap(bitmap);
|
||||
|
||||
if (barcodeFromBitmap.isEmpty()) {
|
||||
Log.i(TAG, "No barcode found in image file");
|
||||
Toast.makeText(context, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
Log.i(TAG, "Read barcode id: " + barcodeFromBitmap.content());
|
||||
Log.i(TAG, "Read format: " + barcodeFromBitmap.format());
|
||||
|
||||
return barcodeFromBitmap;
|
||||
}
|
||||
|
||||
if (requestCode == Utils.BARCODE_SCAN || requestCode == Utils.SELECT_BARCODE_REQUEST) {
|
||||
@@ -314,15 +196,7 @@ public class Utils {
|
||||
Log.i(TAG, "Read barcode id: " + contents);
|
||||
Log.i(TAG, "Read format: " + format);
|
||||
|
||||
LoyaltyCard loyaltyCard = new LoyaltyCard();
|
||||
if (format != null) {
|
||||
loyaltyCard.setBarcodeType(CatimaBarcode.fromName(format));
|
||||
}
|
||||
if (contents != null) {
|
||||
loyaltyCard.setCardId(contents);
|
||||
}
|
||||
|
||||
return Collections.singletonList(new ParseResult(ParseResultType.BARCODE_ONLY, loyaltyCard));
|
||||
return new BarcodeValues(format, contents);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unknown request code for parseSetBarcodeActivityResult");
|
||||
@@ -342,22 +216,22 @@ public class Utils {
|
||||
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), data);
|
||||
}
|
||||
|
||||
static public List<ParseResult> getBarcodesFromBitmap(Bitmap bitmap) {
|
||||
static public BarcodeValues getBarcodeFromBitmap(Bitmap bitmap) {
|
||||
// This function is vulnerable to OOM, so we try again with a smaller bitmap is we get OOM
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
return Utils.getBarcodesFromBitmapReal(bitmap);
|
||||
return Utils.getBarcodeFromBitmapReal(bitmap);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.w(TAG, "Ran OOM in getBarcodesFromBitmap! Trying again with smaller picture! Retry " + i + " of 10.");
|
||||
Log.w(TAG, "Ran OOM in getBarcodeFromBitmap! Trying again with smaller picture! Retry " + i + " of 10.");
|
||||
bitmap = Bitmap.createScaledBitmap(bitmap, (int) Math.round(0.75 * bitmap.getWidth()), (int) Math.round(0.75 * bitmap.getHeight()), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Give up
|
||||
return new ArrayList<>();
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
|
||||
static private List<ParseResult> getBarcodesFromBitmapReal(Bitmap bitmap) {
|
||||
static private BarcodeValues getBarcodeFromBitmapReal(Bitmap bitmap) {
|
||||
// In order to decode it, the Bitmap must first be converted into a pixel array...
|
||||
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
@@ -366,68 +240,15 @@ public class Utils {
|
||||
LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
|
||||
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
||||
List<ParseResult> parseResultList = new ArrayList<>();
|
||||
try {
|
||||
MultiFormatReader multiFormatReader = new MultiFormatReader();
|
||||
MultipleBarcodeReader multipleBarcodeReader = new GenericMultipleBarcodeReader(multiFormatReader);
|
||||
Result barcodeResult = new MultiFormatReader().decode(binaryBitmap);
|
||||
|
||||
Result[] barcodeResults = multipleBarcodeReader.decodeMultiple(binaryBitmap);
|
||||
|
||||
for (Result barcodeResult : barcodeResults) {
|
||||
Log.i(TAG, "Read barcode id: " + barcodeResult.getText());
|
||||
Log.i(TAG, "Read format: " + barcodeResult.getBarcodeFormat().name());
|
||||
|
||||
LoyaltyCard loyaltyCard = new LoyaltyCard();
|
||||
loyaltyCard.setCardId(barcodeResult.getText());
|
||||
loyaltyCard.setBarcodeType(CatimaBarcode.fromBarcode(barcodeResult.getBarcodeFormat()));
|
||||
parseResultList.add(new ParseResult(ParseResultType.BARCODE_ONLY, loyaltyCard));
|
||||
}
|
||||
|
||||
return parseResultList;
|
||||
return new BarcodeValues(barcodeResult.getBarcodeFormat().name(), barcodeResult.getText());
|
||||
} catch (NotFoundException e) {
|
||||
return parseResultList;
|
||||
return new BarcodeValues(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
static public void makeUserChooseParseResultFromList(Context context, List<ParseResult> parseResultList, ParseResultListDisambiguatorCallback callback) {
|
||||
// If there is only one choice, consider it chosen
|
||||
if (parseResultList.size() == 1) {
|
||||
callback.onUserChoseParseResult(parseResultList.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask user to choose a barcode
|
||||
// TODO: This should contain an image of the barcode in question to help users understand the choice they're making
|
||||
CharSequence[] barcodeDescriptions = new CharSequence[parseResultList.size()];
|
||||
for (int i = 0; i < parseResultList.size(); i++) {
|
||||
ParseResult parseResult = parseResultList.get(i);
|
||||
CatimaBarcode catimaBarcode = parseResult.getLoyaltyCard().barcodeType;
|
||||
|
||||
String barcodeContent = parseResult.getLoyaltyCard().cardId;
|
||||
// Shorten overly long barcodes
|
||||
if (barcodeContent.length() > 22) {
|
||||
barcodeContent = barcodeContent.substring(0, 20) + "…";
|
||||
}
|
||||
|
||||
String parseResultNote = parseResult.getNote();
|
||||
|
||||
if (parseResultNote != null) {
|
||||
barcodeDescriptions[i] = String.format("%s: %s (%s)", parseResultNote, catimaBarcode != null ? catimaBarcode.prettyName() : context.getString(R.string.noBarcode), barcodeContent);
|
||||
} else {
|
||||
barcodeDescriptions[i] = String.format("%s (%s)", catimaBarcode != null ? catimaBarcode.prettyName() : context.getString(R.string.noBarcode), barcodeContent);
|
||||
}
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
|
||||
builder.setTitle(context.getString(R.string.multipleBarcodesFoundPleaseChooseOne));
|
||||
builder.setItems(
|
||||
barcodeDescriptions,
|
||||
(dialogInterface, i) -> callback.onUserChoseParseResult(parseResultList.get(i))
|
||||
);
|
||||
builder.setOnCancelListener(dialogInterface -> callback.onUserDismissedSelector());
|
||||
builder.show();
|
||||
}
|
||||
|
||||
static public Boolean isNotYetValid(Date validFromDate) {
|
||||
// The note in `hasExpired` does not apply here, since the bug was fixed before this feature was added.
|
||||
return validFromDate.after(getStartOfToday().getTime());
|
||||
@@ -455,7 +276,6 @@ public class Utils {
|
||||
|
||||
static public String formatBalance(Context context, BigDecimal value, Currency currency) {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
numberFormat.setGroupingUsed(false);
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
@@ -463,7 +283,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
|
||||
currencyFormat.setGroupingUsed(false);
|
||||
currencyFormat.setCurrency(currency);
|
||||
currencyFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
|
||||
currencyFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
|
||||
@@ -473,7 +292,6 @@ public class Utils {
|
||||
|
||||
static public String formatBalanceWithoutCurrencySymbol(BigDecimal value, Currency currency) {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
numberFormat.setGroupingUsed(false);
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
@@ -486,72 +304,19 @@ public class Utils {
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
|
||||
private static final double LargestPreciseDouble = (double) (1l << 53);
|
||||
static{
|
||||
assert (LargestPreciseDouble + 1.0) == LargestPreciseDouble;
|
||||
assert (LargestPreciseDouble - 1.0) != LargestPreciseDouble;
|
||||
}
|
||||
|
||||
private static BigDecimal fromParsed(Number parsed){
|
||||
if(parsed instanceof BigDecimal)
|
||||
return (BigDecimal) parsed;
|
||||
|
||||
final double d = parsed.doubleValue();
|
||||
if(d >= LargestPreciseDouble)
|
||||
return new BigDecimal(parsed.longValue());
|
||||
return new BigDecimal(d);
|
||||
}
|
||||
|
||||
static public BigDecimal parseBalance(String value, Currency currency) throws ParseException {
|
||||
// This function expects the input string to not have any grouping (thousand separators).
|
||||
// It will refuse to work otherwise
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
numberFormat.setGroupingUsed(false);
|
||||
|
||||
if (numberFormat instanceof DecimalFormat) {
|
||||
((DecimalFormat) numberFormat).setParseBigDecimal(true);
|
||||
}
|
||||
|
||||
if (currency == null) {
|
||||
numberFormat.setMaximumFractionDigits(0);
|
||||
} else {
|
||||
int fractionDigits = currency.getDefaultFractionDigits();
|
||||
|
||||
numberFormat.setMinimumFractionDigits(fractionDigits);
|
||||
numberFormat.setMaximumFractionDigits(fractionDigits);
|
||||
|
||||
if (numberFormat instanceof DecimalFormat) {
|
||||
// If the string contains both thousand separators and decimals separators, fail hard
|
||||
DecimalFormatSymbols decimalFormatSymbols = ((DecimalFormat) numberFormat).getDecimalFormatSymbols();
|
||||
char decimalSeparator = decimalFormatSymbols.getDecimalSeparator();
|
||||
|
||||
// Translate all non-digits to decimal separators, failing if we find more than 1.
|
||||
// We loop over the codepoints to make sure eastern arabic numerals are not mistakenly
|
||||
// treated as a separator.
|
||||
boolean separatorFound = false;
|
||||
StringBuilder translatedValue = new StringBuilder();
|
||||
for (int i = 0; i < value.length();) {
|
||||
int character = value.codePointAt(i);
|
||||
|
||||
if (Character.isDigit(character)) {
|
||||
translatedValue.append(value.charAt(i));
|
||||
} else {
|
||||
if (separatorFound) {
|
||||
throw new ParseException("Contains multiple separators", i);
|
||||
}
|
||||
|
||||
separatorFound = true;
|
||||
translatedValue.append(decimalSeparator);
|
||||
}
|
||||
|
||||
i += Character.charCount(character);
|
||||
}
|
||||
|
||||
value = translatedValue.toString();
|
||||
}
|
||||
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
|
||||
numberFormat.setMaximumFractionDigits(currency.getDefaultFractionDigits());
|
||||
}
|
||||
|
||||
return fromParsed(numberFormat.parse(value));
|
||||
Log.d(TAG, numberFormat.parse(value).toString());
|
||||
|
||||
return new BigDecimal(numberFormat.parse(value).toString());
|
||||
}
|
||||
|
||||
static public byte[] bitmapToByteArray(Bitmap bitmap) {
|
||||
@@ -842,7 +607,7 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable Bitmap loadImage(String path) {
|
||||
public static Bitmap loadImage(String path) {
|
||||
try {
|
||||
return BitmapFactory.decodeStream(new FileInputStream(path));
|
||||
} catch (IOException e) {
|
||||
@@ -851,7 +616,7 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable Bitmap loadTempImage(Context context, String name) {
|
||||
public static Bitmap loadTempImage(Context context, String name) {
|
||||
return loadImage(context.getCacheDir() + "/" + name);
|
||||
}
|
||||
|
||||
@@ -928,7 +693,7 @@ public class Utils {
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
public static int getHeaderColorFromImage(@Nullable Bitmap image, int fallback) {
|
||||
public static int getHeaderColorFromImage(Bitmap image, int fallback) {
|
||||
if (image == null) {
|
||||
return fallback;
|
||||
}
|
||||
@@ -983,53 +748,38 @@ public class Utils {
|
||||
.replaceAll("(?<!href=\")\\b(https?://[\\w@#%&+=:?/.-]*[\\w@#%&+=:?/-])", "<a href=\"$1\">$1</a>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an icon or text with background on the given ImageView and/or TextView, including background colour.
|
||||
*
|
||||
* @param context Android context
|
||||
* @param loyaltyCard Loyalty Card
|
||||
* @param icon Bitmap of the icon to set, or null
|
||||
* @param backgroundOrIcon ImageView to draw the icon and background on to
|
||||
* @param textWhenNoImage TextView to write the loyalty card name into if icon is null
|
||||
* @return background colour
|
||||
*/
|
||||
public static int setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage, int columnCount) {
|
||||
int headerColor = getHeaderColor(context, loyaltyCard);
|
||||
backgroundOrIcon.setImageBitmap(icon);
|
||||
|
||||
public static void setIconOrTextWithBackground(Context context, LoyaltyCard loyaltyCard, Bitmap icon, ImageView backgroundOrIcon, TextView textWhenNoImage) {
|
||||
if (icon != null) {
|
||||
// Use header colour to decide if this image will need a white or black background
|
||||
backgroundOrIcon.setBackgroundColor(needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
||||
|
||||
Log.d("onResume", "setting icon image");
|
||||
textWhenNoImage.setVisibility(View.GONE);
|
||||
|
||||
backgroundOrIcon.setImageBitmap(icon);
|
||||
backgroundOrIcon.setBackgroundColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
// Use header colour as background colour
|
||||
backgroundOrIcon.setBackgroundColor(headerColor);
|
||||
|
||||
// Manually calculate how many lines will be needed
|
||||
// This is necessary because Android's auto sizing will split over lines way before reaching the minimum font size and store names split over multiple lines are harder to scan with a quick glance so we should try to prevent it
|
||||
// Because we have to write the text before we can actually know the exact laid out size (trying to delay this causes bugs where the autosize fails) we have to take some... weird shortcuts
|
||||
|
||||
// At this point textWhenNoImage.getWidth() still returns 0, so we cheat by calculating the whole width of the screen and then dividing it by the amount of columns
|
||||
int columnWidth = Resources.getSystem().getDisplayMetrics().widthPixels / columnCount;
|
||||
|
||||
// Calculate how wide a character is and calculate how many characters fit in a line
|
||||
// text size is generally based on height, so setting 1:1 as width may be fishy
|
||||
int characterWidth = TextViewCompat.getAutoSizeMinTextSize(textWhenNoImage);
|
||||
int maxWidthPerLine = columnWidth - textWhenNoImage.getPaddingStart() - textWhenNoImage.getPaddingEnd();
|
||||
|
||||
// Set number of lines based on what could fit at most
|
||||
int fullTextWidth = loyaltyCard.store.length() * characterWidth;
|
||||
int maxLines = (fullTextWidth / maxWidthPerLine) + 1;
|
||||
textWhenNoImage.setMaxLines(maxLines);
|
||||
|
||||
// Actually set the text and colour
|
||||
textWhenNoImage.setVisibility(View.VISIBLE);
|
||||
|
||||
int headerColor = getHeaderColor(context, loyaltyCard);
|
||||
|
||||
backgroundOrIcon.setImageBitmap(null);
|
||||
backgroundOrIcon.setBackgroundColor(headerColor);
|
||||
textWhenNoImage.setText(loyaltyCard.store);
|
||||
textWhenNoImage.setTextColor(Utils.needsDarkForeground(headerColor) ? Color.BLACK : Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
return headerColor;
|
||||
public static boolean installedFromGooglePlay(Context context) {
|
||||
try {
|
||||
String packageName = context.getPackageName();
|
||||
String installer;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
installer = context.getPackageManager().getInstallSourceInfo(packageName).getInstallingPackageName();
|
||||
} else {
|
||||
installer = context.getPackageManager().getInstallerPackageName(packageName);
|
||||
}
|
||||
return installer.equals("com.android.vending");
|
||||
} catch (Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHeaderColor(Context context, LoyaltyCard loyaltyCard) {
|
||||
@@ -1085,12 +835,4 @@ public class Utils {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean deviceHasCamera(Context context) {
|
||||
try {
|
||||
return ((CameraManager) context.getSystemService(Context.CAMERA_SERVICE)).getCameraIdList().length > 0;
|
||||
} catch (CameraAccessException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class ZipUtils {
|
||||
return new JSONObject(read(zipInputStream));
|
||||
}
|
||||
|
||||
public static String read(ZipInputStream zipInputStream) throws IOException {
|
||||
private static String read(ZipInputStream zipInputStream) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Reader reader = new BufferedReader(new InputStreamReader(zipInputStream, StandardCharsets.UTF_8));
|
||||
int c;
|
||||
|
||||
@@ -49,7 +49,7 @@ public class CatimaExporter implements Exporter {
|
||||
// Generate CSV
|
||||
ByteArrayOutputStream catimaOutputStream = new ByteArrayOutputStream();
|
||||
OutputStreamWriter catimaOutputStreamWriter = new OutputStreamWriter(catimaOutputStream, StandardCharsets.UTF_8);
|
||||
writeCSV(context, database, catimaOutputStreamWriter);
|
||||
writeCSV(database, catimaOutputStreamWriter);
|
||||
|
||||
// Add CSV to zip file
|
||||
ZipParameters csvZipParameters = createZipParameters("catima.csv", password);
|
||||
@@ -64,12 +64,12 @@ public class CatimaExporter implements Exporter {
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(database);
|
||||
while (cardCursor.moveToNext()) {
|
||||
// For each card
|
||||
LoyaltyCard card = LoyaltyCard.fromCursor(context, cardCursor);
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
|
||||
// For each image
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
// If it exists, add to the .zip file
|
||||
Bitmap image = card.getImageForImageLocationType(context, imageLocationType);
|
||||
Bitmap image = Utils.retrieveCardImage(context, card.id, imageLocationType);
|
||||
if (image != null) {
|
||||
ZipParameters imageZipParameters = createZipParameters(Utils.getCardImageFileName(card.id, imageLocationType), password);
|
||||
zipOutputStream.putNextEntry(imageZipParameters);
|
||||
@@ -95,7 +95,7 @@ public class CatimaExporter implements Exporter {
|
||||
return zipParameters;
|
||||
}
|
||||
|
||||
private void writeCSV(Context context, SQLiteDatabase database, OutputStreamWriter output) throws IOException, InterruptedException {
|
||||
private void writeCSV(SQLiteDatabase database, OutputStreamWriter output) throws IOException, InterruptedException {
|
||||
CSVPrinter printer = new CSVPrinter(output, CSVFormat.RFC4180);
|
||||
|
||||
// Print the version
|
||||
@@ -142,7 +142,7 @@ public class CatimaExporter implements Exporter {
|
||||
Cursor cardCursor = DBHelper.getLoyaltyCardCursor(database);
|
||||
|
||||
while (cardCursor.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.fromCursor(context, cardCursor);
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor);
|
||||
|
||||
printer.printRecord(card.id,
|
||||
card.store,
|
||||
@@ -176,7 +176,7 @@ public class CatimaExporter implements Exporter {
|
||||
Cursor cardCursor2 = DBHelper.getLoyaltyCardCursor(database);
|
||||
|
||||
while (cardCursor2.moveToNext()) {
|
||||
LoyaltyCard card = LoyaltyCard.fromCursor(context, cardCursor2);
|
||||
LoyaltyCard card = LoyaltyCard.toLoyaltyCard(cardCursor2);
|
||||
|
||||
for (Group group : DBHelper.getLoyaltyCardGroups(database, card.id)) {
|
||||
printer.printRecord(card.id, group._id);
|
||||
|
||||
@@ -124,7 +124,7 @@ public class CatimaImporter implements Importer {
|
||||
Set<String> existingImages = DBHelper.imageFiles(context, database);
|
||||
|
||||
for (LoyaltyCard card : data.cards) {
|
||||
LoyaltyCard existing = DBHelper.getLoyaltyCard(context, database, card.id);
|
||||
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);
|
||||
@@ -152,7 +152,7 @@ public class CatimaImporter implements Importer {
|
||||
}
|
||||
|
||||
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(context, existing, card)) {
|
||||
if (!LoyaltyCard.isDuplicate(existing, card)) {
|
||||
return false;
|
||||
}
|
||||
for (ImageLocationType imageLocationType : ImageLocationType.values()) {
|
||||
@@ -490,29 +490,7 @@ public class CatimaImporter implements Importer {
|
||||
// We catch this exception so we can still import old backups
|
||||
}
|
||||
|
||||
return new LoyaltyCard(
|
||||
id,
|
||||
store,
|
||||
note,
|
||||
validFrom,
|
||||
expiry,
|
||||
balance,
|
||||
balanceType,
|
||||
cardId,
|
||||
barcodeId,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
starStatus,
|
||||
lastUsed,
|
||||
DBHelper.DEFAULT_ZOOM_LEVEL,
|
||||
archiveStatus,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
return new LoyaltyCard(id, store, note, validFrom, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starStatus, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, archiveStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -149,29 +149,7 @@ public class FidmeImporter implements Importer {
|
||||
// TODO: Front and back image
|
||||
|
||||
// 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,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
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) {
|
||||
|
||||
@@ -9,9 +9,8 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import net.lingala.zip4j.ZipFile;
|
||||
import net.lingala.zip4j.io.inputstream.ZipInputStream;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@@ -21,7 +20,9 @@ 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;
|
||||
@@ -129,9 +130,11 @@ public class StocardImporter implements Importer {
|
||||
throw new FormatException("Issue parsing CSV data", e);
|
||||
}
|
||||
|
||||
ZipFile zipFile = new ZipFile(inputFile, password);
|
||||
zipData = importZIP(zipFile, zipData);
|
||||
zipFile.close();
|
||||
InputStream input = new FileInputStream(inputFile);
|
||||
ZipInputStream zipInputStream = new ZipInputStream(input, password);
|
||||
zipData = importZIP(zipInputStream, zipData);
|
||||
zipInputStream.close();
|
||||
input.close();
|
||||
|
||||
if (zipData.cards.keySet().size() == 0) {
|
||||
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
|
||||
@@ -141,7 +144,7 @@ public class StocardImporter implements Importer {
|
||||
saveAndDeduplicate(context, database, importedData);
|
||||
}
|
||||
|
||||
public ZIPData importZIP(ZipFile zipFile, final ZIPData zipData) throws IOException, FormatException, JSONException {
|
||||
public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, FormatException, JSONException {
|
||||
Map<String, StocardRecord> cards = zipData.cards;
|
||||
Map<String, StocardProvider> providers = zipData.providers;
|
||||
|
||||
@@ -149,8 +152,9 @@ public class StocardImporter implements Importer {
|
||||
String[] cardBaseName = null;
|
||||
String customProviderId = "";
|
||||
String cardName = "";
|
||||
for (FileHeader fileHeader : zipFile.getFileHeaders()) {
|
||||
String fileName = fileHeader.getFileName();
|
||||
LocalFileHeader localFileHeader;
|
||||
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
|
||||
String fileName = localFileHeader.getFileName();
|
||||
String[] nameParts = fileName.split("/");
|
||||
|
||||
if (nameParts.length < 2) {
|
||||
@@ -158,7 +162,6 @@ public class StocardImporter implements Importer {
|
||||
}
|
||||
|
||||
String userId = nameParts[1];
|
||||
ZipInputStream zipInputStream = zipFile.getInputStream(fileHeader);
|
||||
|
||||
if (customProvidersBaseName == null) {
|
||||
// FIXME: can we use the points-account/statement/content.json balance info somehow?
|
||||
@@ -299,8 +302,6 @@ public class StocardImporter implements Importer {
|
||||
} else if (!fileName.endsWith("/")) {
|
||||
Log.d(TAG, "Unknown or unused file " + fileName + ", skipping...");
|
||||
}
|
||||
|
||||
zipInputStream.close();
|
||||
}
|
||||
|
||||
return new ZIPData(cards, providers);
|
||||
@@ -354,29 +355,7 @@ public class StocardImporter implements Importer {
|
||||
|
||||
long lastUsed = record.lastUsed != null ? record.lastUsed : Utils.getUnixTime();
|
||||
|
||||
LoyaltyCard card = new LoyaltyCard(
|
||||
tempID,
|
||||
store,
|
||||
note,
|
||||
null,
|
||||
null,
|
||||
BigDecimal.valueOf(0),
|
||||
null,
|
||||
record.cardId,
|
||||
null,
|
||||
barcodeType,
|
||||
headerColor,
|
||||
0,
|
||||
lastUsed,
|
||||
DBHelper.DEFAULT_ZOOM_LEVEL,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
LoyaltyCard card = new LoyaltyCard(tempID, store, note, null, null, BigDecimal.valueOf(0), null, record.cardId, null, barcodeType, headerColor, 0, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, 0);
|
||||
importedData.cards.add(card);
|
||||
|
||||
Map<ImageLocationType, Bitmap> images = new HashMap<>();
|
||||
|
||||
@@ -151,29 +151,7 @@ public class VoucherVaultImporter implements Importer {
|
||||
}
|
||||
|
||||
// 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,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
importedData.cards.add(new LoyaltyCard(-1, store, "", null, expiry, balance, balanceType, cardId, null, barcodeType, headerColor, 0, Utils.getUnixTime(), DBHelper.DEFAULT_ZOOM_LEVEL, 0));
|
||||
}
|
||||
|
||||
return importedData;
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package protect.card_locker.preferences;
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntegerRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
@@ -18,9 +14,8 @@ import protect.card_locker.R;
|
||||
import protect.card_locker.Utils;
|
||||
|
||||
public class Settings {
|
||||
private static final String TAG = "Catima";
|
||||
private final Context mContext;
|
||||
private final SharedPreferences mSettings;
|
||||
private SharedPreferences mSettings;
|
||||
|
||||
public Settings(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
@@ -47,11 +42,10 @@ public class Settings {
|
||||
return mSettings.getBoolean(getResString(keyId), defaultValue);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Locale getLocale() {
|
||||
String value = getString(R.string.settings_key_locale, "");
|
||||
|
||||
if (value.isEmpty()) {
|
||||
if (value.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -97,23 +91,4 @@ public class Settings {
|
||||
public String getColor() {
|
||||
return getString(R.string.setting_key_theme_color, mContext.getResources().getString(R.string.settings_key_system_theme));
|
||||
}
|
||||
|
||||
public int getPreferredColumnCount() {
|
||||
var defaultSymbol = mContext.getResources().getString(R.string.settings_key_automatic_column_count);
|
||||
var defaultColumnCount = mContext.getResources().getInteger(R.integer.main_view_card_columns);
|
||||
var orientation = mContext.getResources().getConfiguration().orientation;
|
||||
var columnCountPrefKey = orientation == ORIENTATION_PORTRAIT ? R.string.setting_key_column_count_portrait : R.string.setting_key_column_count_landscape;
|
||||
var columnCountSetting = getString(columnCountPrefKey, defaultSymbol);
|
||||
try {
|
||||
// the pref may be unset or explicitly set to default
|
||||
return columnCountSetting.equals(defaultSymbol) ? defaultColumnCount : Integer.parseInt(columnCountSetting);
|
||||
} catch (NumberFormatException nfe) {
|
||||
Log.e(TAG, "Failed to parseInt the column count pref", nfe);
|
||||
return defaultColumnCount;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean useVolumeKeysForNavigation() {
|
||||
return getBoolean(R.string.settings_key_use_volume_keys_navigation, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package protect.card_locker.viewmodels
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import protect.card_locker.LoyaltyCard
|
||||
import protect.card_locker.LoyaltyCardField
|
||||
import protect.card_locker.async.TaskHandler
|
||||
|
||||
class LoyaltyCardEditActivityViewModel : ViewModel() {
|
||||
var initialized: Boolean = false
|
||||
var hasChanged: Boolean = false
|
||||
|
||||
var taskHandler: TaskHandler = TaskHandler();
|
||||
|
||||
var addGroup: String? = null
|
||||
var openSetIconMenu: Boolean = false
|
||||
var loyaltyCardId: Int = 0
|
||||
var updateLoyaltyCard: Boolean = false
|
||||
var duplicateFromLoyaltyCardId: Boolean = false
|
||||
var importLoyaltyCardUri: Uri? = null
|
||||
|
||||
var tabIndex: Int = 0
|
||||
var requestedImageType: Int = 0
|
||||
var tempLoyaltyCardField: LoyaltyCardField? = null
|
||||
|
||||
var loyaltyCard: LoyaltyCard = LoyaltyCard()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
|
||||
|
||||
</vector>
|
||||
@@ -1,5 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
|
||||
</vector>
|
||||
@@ -1,5 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM11.5,9.5c0,0.83 -0.67,1.5 -1.5,1.5L9,11v2L7.5,13L7.5,7L10,7c0.83,0 1.5,0.67 1.5,1.5v1zM16.5,11.5c0,0.83 -0.67,1.5 -1.5,1.5h-2.5L12.5,7L15,7c0.83,0 1.5,0.67 1.5,1.5v3zM20.5,8.5L19,8.5v1h1.5L20.5,11L19,11v2h-1.5L17.5,7h3v1.5zM9,9.5h1v-1L9,8.5v1zM4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM14,11.5h1v-3h-1v3z"/>
|
||||
|
||||
</vector>
|
||||
@@ -2,10 +2,9 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:viewportHeight="24"
|
||||
android:tint="@android:color/white">
|
||||
<path
|
||||
android:fillColor="#D3D3D3"
|
||||
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"
|
||||
android:strokeWidth="0.25"
|
||||
android:strokeColor="#777777"/>
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_baseline_archive_24_black.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_archive_24_black.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@android:color/black">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_unfold_less_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_unfold_less_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="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zM16.59,5.41L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_starred_black.xml
Normal file
5
app/src/main/res/drawable/ic_starred_black.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
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,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_starred_white.xml
Normal file
5
app/src/main/res/drawable/ic_starred_white.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
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,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_unstarred_black.xml
Normal file
5
app/src/main/res/drawable/ic_unstarred_black.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
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="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_unstarred_white.xml
Normal file
5
app/src/main/res/drawable/ic_unstarred_white.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
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="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M368,640L480,556L590,640L548,504L660,416L524,416L480,280L436,416L300,416L410,504L368,640ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,585Q80,574 87,566Q94,558 105,556Q129,548 144.5,527Q160,506 160,480Q160,454 144.5,433Q129,412 105,404Q94,402 87,394Q80,386 80,375L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,375Q880,386 873,394Q866,402 855,404Q831,412 815.5,433Q800,454 800,480Q800,506 815.5,527Q831,548 855,556Q866,558 873,566Q880,574 880,585L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,618Q763,596 741.5,559.5Q720,523 720,480Q720,437 741.5,400.5Q763,364 800,342L800,240Q800,240 800,240Q800,240 800,240L160,240Q160,240 160,240Q160,240 160,240L160,342Q197,364 218.5,400.5Q240,437 240,480Q240,523 218.5,559.5Q197,596 160,618L160,720Q160,720 160,720Q160,720 160,720ZM480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
|
||||
</vector>
|
||||
@@ -1,11 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#D3D3D3"
|
||||
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"
|
||||
android:strokeWidth="0.25"
|
||||
android:strokeColor="#777777"/>
|
||||
</vector>
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -21,12 +20,11 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:paddingVertical="8dp"
|
||||
android:clipToPadding="false">
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -37,17 +35,14 @@
|
||||
android:id="@+id/version_history"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version_history_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/version_history"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -57,8 +52,7 @@
|
||||
android:id="@+id/version_history_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/version_history_main" />
|
||||
@@ -80,17 +74,14 @@
|
||||
android:id="@+id/credits"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/credits_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/credits"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -100,8 +91,7 @@
|
||||
android:id="@+id/credits_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/credits_main" />
|
||||
@@ -123,17 +113,14 @@
|
||||
android:id="@+id/translate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/help_translate_this_app"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -143,8 +130,7 @@
|
||||
android:id="@+id/translate_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/translate_platform"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -167,17 +153,14 @@
|
||||
android:id="@+id/license"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/license_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/license"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -187,8 +170,7 @@
|
||||
android:id="@+id/license_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/app_license"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -211,17 +193,14 @@
|
||||
android:id="@+id/repo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/repo_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/source_repository"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -231,8 +210,7 @@
|
||||
android:id="@+id/repo_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/on_github"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -255,17 +233,14 @@
|
||||
android:id="@+id/privacy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privacy_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/privacy_policy"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -275,8 +250,7 @@
|
||||
android:id="@+id/privacy_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/and_data_usage"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -299,21 +273,17 @@
|
||||
android:id="@+id/donate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/donate_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/donate"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
@@ -333,17 +303,14 @@
|
||||
android:id="@+id/rate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rate_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/rate_this_app"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -353,8 +320,7 @@
|
||||
android:id="@+id/rate_sub"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/on_google_play"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -374,20 +340,17 @@
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:padding="8dp"
|
||||
android:id="@+id/report_error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/report_error_main"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:padding="2dp"
|
||||
android:text="@string/report_error"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -399,10 +362,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/report_error_main"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/on_github" />
|
||||
android:text="@string/on_github"
|
||||
android:padding="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:importantForAccessibility="no"
|
||||
@@ -417,5 +379,5 @@
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</ScrollView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
style="?attr/toolbarStyle" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/groups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:tabMode="scrollable" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/textView"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -9,20 +9,20 @@
|
||||
tools:showIn="@layout/custom_barcode_scanner">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/camera_error_clickable_area"
|
||||
android:id="@+id/camera_permission_denied_clickable_area"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/camera_error_icon"
|
||||
android:id="@+id/camera_permission_denied_icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="84dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/camera_error" />
|
||||
android:src="@drawable/camera_permission_denied" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_error_title"
|
||||
android:id="@+id/camera_permission_denied_title"
|
||||
style="@style/TextAppearance.Material3.HeadlineLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -30,12 +30,12 @@
|
||||
android:text="@string/cameraPermissionDeniedTitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_error_message"
|
||||
android:id="@+id/camera_permission_denied_message"
|
||||
style="@style/AppTheme.TextView.NoData"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/zxing_msg_camera_framework_bug" />
|
||||
android:text="@string/noCameraPermissionDirectToSystemSetting" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -34,8 +34,8 @@
|
||||
android:padding="@dimen/activity_scanner_padding">
|
||||
|
||||
<include
|
||||
android:id="@+id/camera_error_layout"
|
||||
layout="@layout/camera_error_layout"
|
||||
android:id="@+id/camera_permission_denied_layout"
|
||||
layout="@layout/camera_permission_failed_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -18,11 +17,11 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
@@ -128,7 +127,7 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/importOptionApplicationButton" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</ScrollView>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
android:paddingRight="8dp"
|
||||
style="@style/Widget.MaterialComponents.Chip.Filter"
|
||||
app:checkedIconVisible="true"
|
||||
android:textAppearance="?android:attr/textAppearance"
|
||||
app:checkedIconTint="?attr/colorOnBackground"/>
|
||||
android:textAppearance="?android:attr/textAppearance" />
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -30,8 +29,7 @@
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent">
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.tabs.TabItem
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -48,10 +46,9 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
<ScrollView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -476,6 +473,6 @@
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
</TableLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -41,10 +41,13 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeMinTextSize="6sp"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:autoSizeMinTextSize="12sp"
|
||||
app:autoSizeMaxTextSize="100sp"
|
||||
app:autoSizeStepGranularity="2sp"
|
||||
android:gravity="center"
|
||||
android:padding="10dp" />
|
||||
android:maxLines="1"
|
||||
android:layout_margin="20dp" />
|
||||
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
@@ -72,15 +75,29 @@
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.8"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/starred"
|
||||
android:elevation="4dp"
|
||||
android:rotationX="2"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/loyalty_card_icon_starred"
|
||||
tools:ignore="ImageContrastCheck"/>
|
||||
app:srcCompat="@drawable/ic_starred_white"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
<ImageView
|
||||
android:importantForAccessibility="no"
|
||||
android:id="@+id/star_border"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/starImage"
|
||||
android:elevation="4dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_unstarred_black"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -96,18 +113,18 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/archive_background"
|
||||
android:layout_width="@dimen/cardThumbnailSize"
|
||||
android:layout_height="@dimen/cardThumbnailSize"
|
||||
android:layout_width="41dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_gravity="end"
|
||||
android:alpha="0.8"
|
||||
android:alpha="0.5"
|
||||
android:contentDescription="@string/archived"
|
||||
android:elevation="4dp"
|
||||
android:rotationX="2"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/loyalty_card_icon_archived"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
app:srcCompat="@drawable/ic_baseline_archive_24"
|
||||
tools:ignore="ImageContrastCheck,MissingConstraints"
|
||||
tools:layout_editor_absoluteX="0dp"
|
||||
tools:layout_editor_absoluteY="-1dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -137,8 +154,6 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:maxLines="5"
|
||||
android:ellipsize="end"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@+id/store"
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_image_description"
|
||||
android:id="@+id/card_id_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -32,7 +31,6 @@
|
||||
android:id="@+id/groups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:tabMode="scrollable"
|
||||
android:visibility="gone"/>
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -27,7 +26,7 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="@integer/main_view_card_columns"
|
||||
app:spanCount="1"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent" />
|
||||
</RelativeLayout>
|
||||
@@ -1,81 +1,56 @@
|
||||
Sylvia van Os
|
||||
Branden Archer
|
||||
J. Lavoie
|
||||
solokot
|
||||
Allan Nordhøy
|
||||
solokot
|
||||
Heimen Stoffels
|
||||
Oğuz Ersen
|
||||
FC (Fay) Stegerman
|
||||
StoyanDimitrov
|
||||
Oğuz Ersen
|
||||
Katharine Chui
|
||||
SlavekB
|
||||
StoyanDimitrov
|
||||
mondstern
|
||||
IllusiveMan196
|
||||
大王叫我来巡山
|
||||
Altonss
|
||||
Michael Moroni
|
||||
Eric
|
||||
GM
|
||||
laralem
|
||||
Eric
|
||||
Petr Novák
|
||||
Joel A
|
||||
B o d o
|
||||
Priit Jõerüüt
|
||||
laralem
|
||||
Taco
|
||||
pfaffenrodt
|
||||
Aayush Gupta
|
||||
Scrambled777
|
||||
Максим Горпиніч
|
||||
ikanakova
|
||||
HudobniVolk
|
||||
Giovanni Donisi
|
||||
Jiri Grönroos
|
||||
Nyatsuki
|
||||
Edgars Andersons
|
||||
Balázs Meskó
|
||||
Milo Ivir
|
||||
Samantaz Fox
|
||||
arno-github
|
||||
Cliff Heraldo
|
||||
Sergio Paredes
|
||||
Ankit Tiwari
|
||||
Silvério Santos
|
||||
josé m
|
||||
Arno-github
|
||||
Jose Delvani
|
||||
mdvhimself
|
||||
Milan Šalka
|
||||
Kachelkaiser
|
||||
Jiri Grönroos
|
||||
Balázs Meskó
|
||||
Giovanni Donisi
|
||||
Milo Ivir
|
||||
Skrripy
|
||||
huuhaa
|
||||
waffshappen
|
||||
Marnick L'Eau
|
||||
ngocanhtve
|
||||
Quentin PAGÈS
|
||||
Projjal Moitra
|
||||
Robin
|
||||
JungHee Lee
|
||||
தமிழ் நேரம்
|
||||
Maksim2005UA
|
||||
Quentin PAGÈS
|
||||
waffshappen
|
||||
Eryk Michalak
|
||||
Ziad OUALHADJ
|
||||
Vasilis
|
||||
Robin Liu
|
||||
Renko
|
||||
mdvhimself
|
||||
Denis Shilin
|
||||
しいたけ
|
||||
Alexander Ivanov
|
||||
Miha Frangež
|
||||
Viet Nguyen Hoang
|
||||
stavpup
|
||||
ehrt74
|
||||
Silvério Santos
|
||||
ikanakova
|
||||
Virginie
|
||||
Tim Trek
|
||||
MisterCosta96
|
||||
arshbeerSingh
|
||||
Augustin LAVILLE
|
||||
Freddo espresso
|
||||
Govind S Nair
|
||||
Kim Seohyun
|
||||
rudy3
|
||||
Ricky Tigg
|
||||
Michael Gangolf
|
||||
Peter Dave Hello
|
||||
rudy3
|
||||
Kim Seohyun
|
||||
Govind S Nair
|
||||
Freddo espresso
|
||||
arshbeerSingh
|
||||
Alexander Ivanov
|
||||
|
||||
@@ -13,7 +13,6 @@ _id,name,barcodeFormat
|
||||
015cf86e-c4b6-42b5-abed-5821492b2669,Campbells,ITF
|
||||
016c8380-d433-4eb1-b7a0-df6fd9254ec6,Friendlies Pharmacy,CODE_128
|
||||
0189b6a0-3f02-418f-872e-d5e354619a45,Mencke Gartencenter,EAN_8
|
||||
01b239f4-d1db-4311-a33b-bc8bb9c71c19,McEwan,CODE_128
|
||||
01ce8326-50e8-4787-9999-e509dfed15cb,Вигода Вопак,CODE_128
|
||||
01eafcc6-ee41-447f-bbce-7a93ffb90b6c,Mario Mikke,EAN_13
|
||||
01f88e2d-3eb4-4242-a32b-1a847a28e140,Crodux,CODE_128
|
||||
@@ -32,7 +31,6 @@ _id,name,barcodeFormat
|
||||
037f2420-273c-4ffe-9dd3-af22868b1b59,Al Pentolone,EAN_13
|
||||
038516b8-3cdd-4f96-9582-97caf9dc3a47,Dier Specialist,CODE_39
|
||||
039784f4-4fef-497e-8f03-f026655394ef,террапевтика,EAN_13
|
||||
039932ff-caec-4d40-aa9a-0ed185b5cf5f,FNV,CODE_128
|
||||
03b89b04-69cd-43cf-88eb-35760f092488,Мегаполис,CODE_128
|
||||
03d62f02-8266-493b-b4fd-95d5c853b87b,мта,EAN_13
|
||||
03fd0d65-b3dd-427b-9f7c-3554fe3dc99b,Happy Sport,EAN_13
|
||||
@@ -62,7 +60,6 @@ _id,name,barcodeFormat
|
||||
0777b427-2af5-4531-81c3-f7421dde9d63,Евразия Автозапчасти,EAN_13
|
||||
078a5228-818d-4a86-8726-c71dd27a3fdc,EU COVID-19 Certificado de Vacunacion,QR_CODE
|
||||
078fdcef-2e8a-4179-befe-5959cd588a7e,Клякса,EAN_13
|
||||
07a90343-0b80-4cb4-8571-b6a2419cff6e,Maracatú,CODE_128
|
||||
07f645dc-3127-4050-94ac-41f42cacdb74,Cats & Dogs,EAN_8
|
||||
081924f1-3eff-480a-a8a9-ec08eb4b75e7,Rossetti Market,EAN_13
|
||||
0821c8d1-4556-4178-af1b-fe4d1977127d,Feedo,CODE_128
|
||||
@@ -87,14 +84,11 @@ _id,name,barcodeFormat
|
||||
09e1c670-eac2-4077-8a66-b990c3ba1ed8,Gamble & Brown Cafe,CODE_39
|
||||
09e38952-3559-4432-821a-84fdee4923f8,Стройка,EAN_13
|
||||
0a047088-f9f9-47c5-a982-b307122f09fa,IGA Rewards,EAN_13
|
||||
0a058735-ecfd-4278-ae7a-9f6917193a3d,JBs Power Centre,CODE_128
|
||||
0a124613-4513-4a4f-b89a-6c4b645e395b,BoniChoix,CODE_128
|
||||
0a6c06b6-056d-4bf2-ae78-915a8c52d464,волгорост,EAN_13
|
||||
0a7c000b-39eb-4464-bc41-03d0e1f4a20f,Life Pharmacy ,CODE_128
|
||||
0ae08429-e2a2-4fe0-840a-e940ce9fd3e5,Zebra,EAN_13
|
||||
0b2502b7-f8d7-426e-b518-4482ee6115eb,Лоза,EAN_13
|
||||
0b4c67fb-bf76-46e8-9a3b-cb0acfe47e71,Giocheria,CODE_39
|
||||
0b539afa-e6b5-42a0-8f03-50d5de9f4af0,MediaMarkt Club Karte,QR_CODE
|
||||
0b600df8-f694-49d5-b5ee-56d0b47ab1bc,reima,EAN_13
|
||||
0b82965b-29df-4c9e-ae5f-70a5d10f1d32,Fanølinjen,CODE_128
|
||||
0bb951c2-c644-4a0b-92c0-754d739a55be,ZALY,EAN_13
|
||||
@@ -134,9 +128,8 @@ _id,name,barcodeFormat
|
||||
0f650862-0a1c-4596-b2f9-30fc8d3bf8d3,Lila Bäcker,QR_CODE
|
||||
0f69ba3f-6084-49a5-b959-24277008de45,CJ Express,CODE_128
|
||||
0f936e1f-b3ac-4a34-aad7-a18bd76150f2,FOTOLAB,CODE_128
|
||||
0fafa67a-b4d2-4365-9f68-c167d43c7070,I TOURS,CODE_128
|
||||
0fce03a0-6b7b-427c-a483-26a1169e73b0,EDMINS,EAN_13
|
||||
1,Accor Live Limitless,QR_CODE
|
||||
1,Accor Le Club,QR_CODE
|
||||
10,Aeroplan,CODE_128
|
||||
100,Esprit,ITF
|
||||
1000,Chemmart Pharmacy,CODE_128
|
||||
@@ -472,7 +465,6 @@ _id,name,barcodeFormat
|
||||
13,Amavita,EAN_13
|
||||
130,GNC,UPC_A
|
||||
1300,IZOD,CODE_128
|
||||
13004ca8-9095-40c2-aa98-1fcf6410efc7,Max Shop,CODE_128
|
||||
1301,La Quinta Inns,CODE_128
|
||||
1302,Pet Supplies Plus,UPC_A
|
||||
1303,Piazza Italia,EAN_13
|
||||
@@ -509,8 +501,8 @@ _id,name,barcodeFormat
|
||||
1331,Bizzbee,QR_CODE
|
||||
1332,Blue Box,CODE_39
|
||||
1333,Brice,EAN_13
|
||||
1334,Tecnomat,GS1_128
|
||||
1335,Bricomarché,GS1_128
|
||||
1334,Bricoman,GS1_128
|
||||
1335,Brico Marché,GS1_128
|
||||
1336,Camaieu,CODE_128
|
||||
1337,Casino Supermarchés,EAN_13
|
||||
1338,Castorama,CODE_128
|
||||
@@ -688,7 +680,7 @@ _id,name,barcodeFormat
|
||||
148f7495-e6f2-40b1-80cd-99b3632cb976,Slam,ITF
|
||||
149,Höffner,ITF
|
||||
1490,Basko,EAN_13
|
||||
1491,Unes,CODE_128
|
||||
1491,Unes,EAN_13
|
||||
1492,Grande Cinema 3,EAN_13
|
||||
1493,Eurobrico,EAN_13
|
||||
1494,Isola dei Tesori,EAN_13
|
||||
@@ -974,7 +966,7 @@ _id,name,barcodeFormat
|
||||
172,Jost,ITF
|
||||
1720,Wheelup,CODE_39
|
||||
1721,BIG4,CODE_128
|
||||
1722,Besson Chaussures,CODE_128
|
||||
1722,Besson Chaussures,EAN_13
|
||||
1723,Cactus,EAN_13
|
||||
1724,Idea Bellezza,CODE_39
|
||||
1725,Uyum,CODE_128
|
||||
@@ -991,7 +983,7 @@ _id,name,barcodeFormat
|
||||
1733,Mondial Tissus,EAN_13
|
||||
1734,Furet du nord,EAN_13
|
||||
1735,Maxxess,EAN_13
|
||||
1736,Des Marques et Vous,EAN_13
|
||||
1736,Devianne,EAN_13
|
||||
1737,Colruyt,ITF
|
||||
1738,Paul,EAN_13
|
||||
1739,JouéClub,EAN_13
|
||||
@@ -1052,7 +1044,7 @@ _id,name,barcodeFormat
|
||||
179,Kastner & Öhler,EAN_13
|
||||
1790,MY SIZE,CODE_39
|
||||
1791,PetO,CODE_128
|
||||
1792,Aveve,EAN_13
|
||||
1792,AVEVE,EAN_13
|
||||
1793,BIO-Planet,ITF
|
||||
1794,Brico,EAN_13
|
||||
1795,Club,CODE_128
|
||||
@@ -1351,10 +1343,9 @@ _id,name,barcodeFormat
|
||||
1e43877a-d4f1-4bff-bdb9-cd3346082a46,Scorpion Bay,EAN_13
|
||||
1e9469a4-8388-4ca9-a463-95ee73a0d953,FAMO,EAN_13
|
||||
1e9a127a-0451-4565-9560-eaa097d3808b,Grill'd,CODE_128
|
||||
1ed46ee6-993a-4053-a016-a0d67e26b91b,Lidl,CODE_128
|
||||
1ed46ee6-993a-4053-a016-a0d67e26b91b,Lidl SK,CODE_128
|
||||
1f01c3b1-08f7-4365-a0f9-f1c9bcbdf58a,Fresco,CODE_128
|
||||
1f15d8f3-c35c-46d6-8038-4c9f91a18909,Покров,EAN_8
|
||||
1f1ec99d-c8c6-42d3-ac6a-b9658a6e0a0d,xBarvy,EAN_13
|
||||
1f661d7a-d355-4590-8d33-0d61630958cc,NDG,CODE_39
|
||||
1f6624c6-5acc-4983-ac17-31b9004232d7,Afvalpas Rijssen-Holten,QR_CODE
|
||||
1f69337f-7604-4e7a-9031-f0ab182e7cd7,Дешёвая Аптека Вита,CODE_128
|
||||
@@ -1462,7 +1453,7 @@ _id,name,barcodeFormat
|
||||
2085,Billa,EAN_13
|
||||
2086,Billa,EAN_13
|
||||
2087,BIPA,EAN_13
|
||||
2088,PENNY,EAN_13
|
||||
2088,Penny,EAN_13
|
||||
2089,Penny,EAN_13
|
||||
209,MCard,CODE_128
|
||||
2090,Shoprite,CODE_128
|
||||
@@ -1501,7 +1492,6 @@ _id,name,barcodeFormat
|
||||
2112,Lindex,CODE_128
|
||||
2113,Twilfit,CODE_128
|
||||
2114,aClass,CODE_128
|
||||
21143721-38a4-466f-b04d-a3e90cb62bad,L'angolo,CODE_128
|
||||
2115,Clas Ohlson,CODE_128
|
||||
2116,Agrimarket,CODE_128
|
||||
2117,Starkki,CODE_128
|
||||
@@ -1610,7 +1600,7 @@ _id,name,barcodeFormat
|
||||
2201,Avance,CODE_128
|
||||
2202,berca.be,EAN_13
|
||||
2203,Brantano,EAN_13
|
||||
2204,Brooklyn nv,EAN_13
|
||||
2204,Brooklyn,EAN_13
|
||||
2205,CAMELEON,CODE_128
|
||||
2206,Carmi,CODE_39
|
||||
2207,E5 mode,ITF
|
||||
@@ -1950,7 +1940,7 @@ _id,name,barcodeFormat
|
||||
2488,Proximus,CODE_128
|
||||
2489,RS Bútor,CODE_128
|
||||
248957ba-dbad-414e-86e4-009fc4e5beee,Самоцветы плюс,ITF
|
||||
249,Woolworths,CODE_128
|
||||
249,Countdown,CODE_128
|
||||
2490,SEIBU PRINCE CLUB,CODE_128
|
||||
2491,サミット,EAN_13
|
||||
2492,The PUB,CODE_128
|
||||
@@ -2034,7 +2024,6 @@ _id,name,barcodeFormat
|
||||
2557,Artex Fashion,EAN_13
|
||||
2558,Askot,CODE_128
|
||||
2559,BUTIK,EAN_8
|
||||
255d84f7-144d-4d63-b6fd-f00a8e94641f,HUK Autowelt,QR_CODE
|
||||
256,Palmers,EAN_13
|
||||
2560,Dayli,EAN_13
|
||||
2561,De Banier,CODE_128
|
||||
@@ -2451,7 +2440,6 @@ _id,name,barcodeFormat
|
||||
28a46b11-8c45-4b2a-93dd-b7325a2fe013,Dialogues,CODE_128
|
||||
28b5866e-f195-4d68-b8a0-02cdb611af4f,Да Здоров! аптека,EAN_13
|
||||
28c5ee9a-cf66-4add-b71c-70b66be85570,Agraria,EAN_13
|
||||
28cc5dc7-61b4-4c95-a5a6-e125cc4bce9b,Aventurx,CODE_128
|
||||
28d93baa-c331-4df8-a85d-65eb86199732,Solar Studio,CODE_128
|
||||
28fbdd64-8715-4cdc-8c3f-df7259b1ba65,NOHO,EAN_13
|
||||
29,Heathrow Rewards,CODE_128
|
||||
@@ -2594,7 +2582,6 @@ _id,name,barcodeFormat
|
||||
2b1eb78e-9684-4434-ba9b-41f00fc5beab,Sensation Profumerie,EAN_13
|
||||
2b29bfc0-26a7-44cb-9d21-2a0bdb467320,Vertex Hotel,ITF
|
||||
2b39b807-6375-404c-bfd7-7f3135654258,Планета Игрушек,EAN_13
|
||||
2b6062ec-39b1-4ac4-b6d6-cf19048c9f3f,Coripet,UPC_A
|
||||
2b6992d5-615a-423a-b196-ab19a418686f,Mimco,CODE_128
|
||||
2b7d84ce-c573-44ea-8989-b23a13cf389b,Азбука Красоты,EAN_13
|
||||
2bc9768c-56a2-4d7d-8f1c-0be9f208b71b,Profile,CODE_128
|
||||
@@ -2865,7 +2852,6 @@ _id,name,barcodeFormat
|
||||
3199,Navyboot,EAN_13
|
||||
31d21202-2674-4c42-9a7e-a19b01d32b63,Vegetalis,EAN_13
|
||||
31d3cf0c-7522-4035-9256-7a712cb1a8b3,Канцелярия,EAN_13
|
||||
31db4e18-fb97-43d2-b026-c41f39d2faba,Bershka,CODE_128
|
||||
31eccc6d-babd-4fee-9ae8-db9a00fc1c63,Pharmactiv,EAN_13
|
||||
31f60f6d-633f-42af-b387-e5d0b4e2f45f,SPINNS,EAN_13
|
||||
32,Bauking,EAN_13
|
||||
@@ -3106,7 +3092,6 @@ _id,name,barcodeFormat
|
||||
3399,Taxi Jetax,CODE_128
|
||||
339bb076-12fd-4e56-899f-3acb79f5da53,Hafenhotel Meereszeiten,CODE_128
|
||||
33a430e4-35c7-43e7-98e8-5ce5d039ee70,VPZ,CODE_128
|
||||
33cb4886-5d06-473a-80b7-980ca2fb27c2,Bouwcenter Nobel,EAN_13
|
||||
33d16d2d-f51e-44c3-92d8-2c3616af2d0f,Apotheke Peer Farmacia,CODE_128
|
||||
33dea27e-c7a4-4e40-8621-32da990f7d82,EU COVID-19 Vaccinationsintyg - Andra vaccination Skott,QR_CODE
|
||||
33e82e4f-5541-4be1-aa4c-0f2987cfd78f,Данди,EAN_13
|
||||
@@ -3467,7 +3452,6 @@ _id,name,barcodeFormat
|
||||
37,Bessmann,ITF
|
||||
370,Virgin Atlantic,CODE_128
|
||||
3700,Go Auto,CODE_128
|
||||
37003c25-7bc7-4dd9-8a3a-8406005d0dcf,Scouts en Gidsen Vlaanderen,CODE_128
|
||||
3701,Good Earth,CODE_128
|
||||
3702,Hachem,CODE_128
|
||||
3703,Le Magasin,CODE_128
|
||||
@@ -3965,7 +3949,6 @@ _id,name,barcodeFormat
|
||||
4083,Каляев,EAN_13
|
||||
4084,Shingle Inn,CODE_128
|
||||
4085,Golden Casket,CODE_128
|
||||
40853977-7fdb-4815-a64e-85d2c70df347,OROCAJA,CODE_39
|
||||
4086,Pet City,CODE_128
|
||||
4087,chempro,EAN_13
|
||||
4088,merlo,CODE_39
|
||||
@@ -4330,7 +4313,7 @@ _id,name,barcodeFormat
|
||||
4387,Kremer,EAN_13
|
||||
4388,Gartencenter Nickl,EAN_13
|
||||
4389,Panarottis,QR_CODE
|
||||
439,Volare ITA airways.,CODE_128
|
||||
439,Alitalia,CODE_128
|
||||
4390,Simply Asia,CODE_128
|
||||
4391,Ultraliquors,CODE_128
|
||||
4392,Cum Books,CODE_128
|
||||
@@ -4582,7 +4565,6 @@ _id,name,barcodeFormat
|
||||
4599,Мокрый Нос,EAN_13
|
||||
45b55fa2-835b-4ae5-a318-16a66b4ec85b,Євро Мікс,EAN_8
|
||||
45cbba3f-f0d2-4837-8189-16b0ff2707f5,Барс,CODE_128
|
||||
45e6b637-a991-45ce-b72d-8f4df03d9f6b,Tradition,CODE_128
|
||||
45e6f6d3-e688-40f7-86e2-73e3803c86bd,KüstenCard mini/maxi,CODE_128
|
||||
45fa81a4-657e-414c-89ed-ebf1c49c0926,G'DAY REWARDS,CODE_128
|
||||
45faf9e5-321c-44a7-b641-7acee8126349,EU COVID-19 Vaccinatiebewijs - Eerste vaccinatieschot,QR_CODE
|
||||
@@ -4790,7 +4772,6 @@ _id,name,barcodeFormat
|
||||
4773,Maximiles,CODE_128
|
||||
4774,La Compagnie des Petits,CODE_128
|
||||
4775,Totem Family,CODE_128
|
||||
477515a9-2257-4d19-af18-3dbcfeb4acd9,Omni,CODE_128
|
||||
4776,La Jardinerie,CODE_128
|
||||
4777,La Plateforme du Bâtiment,EAN_13
|
||||
4778,Animal & Co,EAN_13
|
||||
@@ -5068,7 +5049,6 @@ _id,name,barcodeFormat
|
||||
4adaa99b-282d-4abe-87c8-b16d3958f4c2,Тюменский ЦУМ,CODE_39
|
||||
4ae5d40d-45ea-4188-bce8-eb3337733466,Garden Floridea,CODE_128
|
||||
4b197111-0d79-4ac5-aecd-5dca6643e390,Евродом,EAN_13
|
||||
4b50787c-052c-48e9-8bae-b01373cef1b8,Fbo Clothing,CODE_128
|
||||
4b511f9a-5c9c-4b9f-8c71-1631cb78456a,Семейная Аптека,EAN_13
|
||||
4b8e7174-b85b-4b82-99ab-b1faee2dfb8f,Diper,EAN_13
|
||||
4ba9de66-0015-49e1-a0d1-d24c2328eaa5,Witchery,EAN_13
|
||||
@@ -5083,18 +5063,14 @@ _id,name,barcodeFormat
|
||||
4ccb26a9-3a58-487f-9bdf-5cc4b042c0b3,UNCS,CODE_128
|
||||
4cd0da27-9a71-4eb0-88f4-23919b598828,Pins,CODE_128
|
||||
4d28254f-9ec6-4262-aa28-ee0bd7620b00,Леонардо,EAN_13
|
||||
4d4102e9-115a-4695-b764-c5534e1749a8,twd,EAN_13
|
||||
4d7b0d6e-2680-4c6b-bdac-8985df7aa8a3,大昌,EAN_13
|
||||
4d8c62b4-b4c5-40b0-9117-6e5022cf7950,MilleMiglia,CODE_128
|
||||
4dab7847-f728-4c34-80ea-a464238a3756,Волна,EAN_13
|
||||
4db2f926-b58d-4821-8f85-b02d3e32fbcb,Дом посуды,EAN_13
|
||||
4dd50f0e-05a1-4a32-97c2-1e5b570d0d9b,MIA,EAN_13
|
||||
4dd586bf-d2ed-4357-898c-11b648bcb796,Детский парк,EAN_13
|
||||
4dd5aa56-2f5c-4bb5-a281-211bb4e5463e,Joylab,CODE_128
|
||||
4e090085-f5bc-4f29-abcf-bb249dd3429d, SSENSE,CODE_128
|
||||
4e1001a2-a664-4d37-8b85-a71b02f9f6dc,xFarby,EAN_13
|
||||
4e24761b-17a7-4b7d-b04a-16f54076d03b,Forum+,EAN_13
|
||||
4e6622db-6fd3-405e-a60e-7157984da5ba,KiemKracht VZW,CODE_128
|
||||
4e95cfa4-3011-41c2-ad87-0c560cbd218c,Lincolnshire Co-operative,DATA_MATRIX
|
||||
4eb5bcd8-9467-44ce-b54c-fc69521431be,Мир Обоев,CODE_128
|
||||
4ed66bc0-04ee-458b-aac7-6bb7bdd35e5c,Пивотека,CODE_39
|
||||
@@ -5324,7 +5300,6 @@ _id,name,barcodeFormat
|
||||
519,Alimerka,CODE_128
|
||||
5190,Souris Mini,CODE_128
|
||||
5191,Лакомка,EAN_13
|
||||
51917108-3469-4067-b1da-8697d60fcfa6,Kingston Frontenac Public Library,CODE_128
|
||||
5192,AlphaZoo,CODE_128
|
||||
5193,БИГАМ,EAN_8
|
||||
5194,Sebastiano,EAN_13
|
||||
@@ -5431,7 +5406,6 @@ _id,name,barcodeFormat
|
||||
55cfc40e-469f-485f-ab26-823014fd8401,Seebauer,EAN_13
|
||||
55db252f-70a8-4da7-b0c2-484c8445e750,Kreativmarkt Hamburg,EAN_13
|
||||
55e96a49-7157-43cc-aaa7-9867d37cb05f,Народная линия,EAN_13
|
||||
55eb9a72-cd1d-49f7-aec1-1f44f6207983,Lina Giorgi snc,CODE_39
|
||||
55f414b7-b1a8-46f6-97ad-7f4f0867d8a9,EU COVID-19 Rokotustodistus - Toinen rokotus laukaus,QR_CODE
|
||||
56,Brax,CODE_128
|
||||
560,Punt Roma,CODE_128
|
||||
@@ -5518,7 +5492,6 @@ _id,name,barcodeFormat
|
||||
5afc2de6-6129-43f5-9caf-be3572d65a90,Sisal,CODE_128
|
||||
5b01f59e-97db-4105-9aab-94f56099fc49,real,GS1_128
|
||||
5b1da0f0-143e-492d-83a9-ad22957a54c6,Metro Lifestyle,CODE_39
|
||||
5b502f6e-7c38-4708-ae56-04f97638692a,Баня Стил,CODE_128
|
||||
5bb5ea85-8952-474e-be53-c5ac11f7428f,Farmec,EAN_13
|
||||
5bb6dc04-3000-475f-a5d4-ba9427989809,Bimbostore Toys Center,EAN_13
|
||||
5bf3f149-2217-45aa-b61b-eec9aeedf5d2,Werdich,CODE_39
|
||||
@@ -5536,7 +5509,6 @@ _id,name,barcodeFormat
|
||||
5d3de23f-b72e-4920-9e3b-1a413979a779,CityCard,CODE_128
|
||||
5d426084-854e-493e-a10d-7ce5d34d31fe,Farmacie Comunali Firenze,CODE_128
|
||||
5d51a06c-3af4-4400-9776-e3458190be87,Parisnail,EAN_13
|
||||
5d5d4520-ee6c-45ea-b5f1-11282a0673f4,Arriva,CODE_128
|
||||
5d695da3-f47b-4da8-b5ff-ea9d0fd9486b,Belaton,CODE_128
|
||||
5d866631-9858-4393-a5cf-eba96ca066cc,Kiwisun,CODE_128
|
||||
5db03921-3703-40d3-ba27-f7d3ff5a40ba,Prodor Supermarché et Boucherie,EAN_13
|
||||
@@ -5546,7 +5518,6 @@ _id,name,barcodeFormat
|
||||
5e18e98b-ad75-426a-a4ac-a80496906906,Beauty X,EAN_13
|
||||
5e27a7ae-ad95-4cce-b383-85a4eb822eaa,Supra Baby,EAN_13
|
||||
5e402125-50f9-4de9-8769-ce4e0dc1d1a1,Romaest,CODE_128
|
||||
5e46de16-6ebf-4d17-933f-2f782df8b3fb,Prima Company,CODE_128
|
||||
5e6edac6-a458-4488-861c-f8f403f4b1e1,MABÙ,QR_CODE
|
||||
5ee2ee34-5027-4535-a55f-657c1a092d5d,Lady Sharm,CODE_128
|
||||
5f01e866-3ef8-46e4-a40a-555594849eb7,ЦУМ,CODE_128
|
||||
@@ -5561,7 +5532,6 @@ _id,name,barcodeFormat
|
||||
6,ACS,CODE_128
|
||||
60,Transgourmet,EAN_13
|
||||
600,Humanic,ITF
|
||||
60046ae3-b41c-4a08-a012-d8e921e8aab0,Multaparts,CODE_128
|
||||
600bf563-b7b2-488a-9e21-0ccc63a67b1d,LAUF!,EAN_13
|
||||
601,Beauty Alliance,CODE_128
|
||||
6014a435-c656-4bf7-bcd6-fa46ed28bac0,Окраина,EAN_13
|
||||
@@ -5584,12 +5554,10 @@ _id,name,barcodeFormat
|
||||
61,Centro,EAN_13
|
||||
610,CAA,CODE_128
|
||||
611,Calgary Co-op,EAN_13
|
||||
6110d522-b979-46ca-a313-ded4eac7db71,Telecomshop Twente,CODE_128
|
||||
612,Canada Post,CODE_128
|
||||
613,Canadian Tire,CODE_128
|
||||
614,Change Lingerie,CODE_39
|
||||
615,SCENE,CODE_128
|
||||
615a7629-0f60-4613-b41a-e1f571f5c20a,Goelia,CODE_128
|
||||
615ddf35-4934-4442-b4df-54b065184476,Сигма,EAN_13
|
||||
616,Denny's,CODE_128
|
||||
617,DeSerres,CODE_128
|
||||
@@ -5641,7 +5609,6 @@ _id,name,barcodeFormat
|
||||
639,National Car Rental,CODE_39
|
||||
63ace5b1-39bb-4486-87a8-692caab2c76b,куулклевер,QR_CODE
|
||||
63ad5b7e-ab54-45f2-9224-2da0122a21eb,Forum TC,EAN_13
|
||||
63b32bf3-2e99-4487-bc45-7b70132fe53c,Checkers,CODE_128
|
||||
63bcf094-bbc1-4caa-adfb-b6e015295f43,Парфюм Лидер,EAN_13
|
||||
63bee835-2e9d-4656-b7b6-4b9e9a024470,Арт-Квартал,EAN_13
|
||||
63c87418-cb15-4294-a872-035a03da3a62,Belleplant,EAN_13
|
||||
@@ -5677,7 +5644,7 @@ _id,name,barcodeFormat
|
||||
657d61fe-7714-4aed-a3d5-6c718c6e9c2a,EU COVID-19 Vaccinationsattest - Første vaccinationsskud,QR_CODE
|
||||
658,Thrifty Foods,CODE_128
|
||||
659,Trade Secret,UPC_A
|
||||
659c40c9-f997-44a8-b6a8-a29df616c4b2,Alfa-Tec,EAN_13
|
||||
659c40c9-f997-44a8-b6a8-a29df616c4b2, Alfa-Tec,EAN_13
|
||||
65e6e477-57a3-41c1-88b2-330a6d0cf8bd,Nobis,PDF_417
|
||||
65e848d6-edd5-401e-9b12-952a5c6fdf47,Джерела Здоров'я,CODE_39
|
||||
66,BCF,CODE_128
|
||||
@@ -5685,7 +5652,6 @@ _id,name,barcodeFormat
|
||||
661,WestJet Rewards,CODE_128
|
||||
66104d31-9ae9-440d-b316-0d07a4319af3,Farma Fedeltà,CODE_128
|
||||
662,Würzenbach Drogerie,EAN_13
|
||||
662e6cc0-3ebe-47db-badf-b31b626ea70c,The Papanui Club,QR_CODE
|
||||
66335d92-4622-4334-8384-4a6d5f61f239,Zinger,EAN_13
|
||||
664,American Eagle,ITF
|
||||
665,TJX Style+,CODE_128
|
||||
@@ -5730,7 +5696,6 @@ _id,name,barcodeFormat
|
||||
687,Thai - Royal Orchid Plus,PDF_417
|
||||
688,SportIT,EAN_13
|
||||
689,Foster Calzature,EAN_13
|
||||
68ac6315-08c6-471d-b2e0-ad42d1a091c8,100 Vetrine,UPC_A
|
||||
68c2495e-937d-4e71-a4ad-85f066df0339,Jardival,EAN_13
|
||||
68c69327-cce9-4de8-a062-b895c062ee60,Iden,EAN_13
|
||||
68d4b527-e419-4346-8078-a4ef07a04f00,Lehner Versand,CODE_128
|
||||
@@ -5764,7 +5729,6 @@ _id,name,barcodeFormat
|
||||
6a5ac3f8-04cb-4d14-884f-1231b72228e8,Топаз,EAN_13
|
||||
6a7b1bc8-eca7-4323-9080-68af9414254f,CastoPro,CODE_128
|
||||
6a85186a-bfd9-4078-a5da-db1b4e1fb526,Molders,CODE_128
|
||||
6a8a8971-821c-46ce-a638-1a8585c9dedd,Booking.com,CODE_128
|
||||
6aa89061-d0b5-46a2-9019-b1cb7146e485,Just Plastics,CODE_128
|
||||
6aa9bd9a-b099-4997-9fa1-b0a7525c6ec7,AZ Casa,EAN_13
|
||||
6ab113ff-77e9-4029-9b23-e420eda105e3,Ehrmann,CODE_39
|
||||
@@ -5803,7 +5767,6 @@ _id,name,barcodeFormat
|
||||
6faff0bd-9236-41f8-9c67-7b546c68085a,BVS,EAN_13
|
||||
6fb31971-1cf0-468e-9f85-ebf6133ad3aa,у Палыча,CODE_128
|
||||
6fb45bab-d4be-49fd-8b58-d841110eb0cb,AL 48,EAN_13
|
||||
6fb4ec1e-c6b7-4597-82a3-5c8d4d69ad4f,Rachelle Béry,CODE_128
|
||||
6fe38419-76d2-4b5c-983e-6dbed7822d62,GiorgioMare,CODE_128
|
||||
6fea059e-d9ec-4063-8ea4-cba5ac035942,L'arca di Noè,EAN_8
|
||||
6ff46a57-e3c9-457e-bfb4-aa922c4c41b4,BENZ,CODE_128
|
||||
@@ -5892,7 +5855,6 @@ _id,name,barcodeFormat
|
||||
740308f3-fda8-4b83-9d86-d13592ef30ab,Dress Code,EAN_13
|
||||
741,O'STIN,EAN_13
|
||||
74135c63-c1ab-47b8-8d99-4d9dcf602eda,VOIX INTERIORS,CODE_128
|
||||
7415ddc5-3d77-410c-a6f8-ab399518a82c,Tradition,CODE_128
|
||||
742,Reebok,CODE_128
|
||||
742069df-a468-45d5-8cf6-cc152b4aefaf,Bacher Garten-Center,EAN_13
|
||||
743,Savage,CODE_128
|
||||
@@ -5940,7 +5902,6 @@ _id,name,barcodeFormat
|
||||
764,Васаби,CODE_128
|
||||
7648aaa6-671e-4396-9e4e-759aa66c9f4f,Bouwcenter,EAN_13
|
||||
7649e44e-66e4-4af1-a913-87a40c8ae739,Office Centre,CODE_128
|
||||
764a67a4-8087-41d1-b53a-d73b8380d5cf,Handy Home,CODE_128
|
||||
765,Вестер,CODE_128
|
||||
766,Виктория,EAN_13
|
||||
767,Газпром АЗС,EAN_13
|
||||
@@ -5973,7 +5934,6 @@ _id,name,barcodeFormat
|
||||
780bd58f-acbb-493c-869d-63f7a93292f3,Schnitz,CODE_128
|
||||
781,Кофе Хауз,CODE_128
|
||||
782,Красный Куб,CODE_128
|
||||
78242148-6c07-4698-9ec1-56017dc687b6,Ideacasa Mercatone,EAN_13
|
||||
782b0597-f7e4-4509-ba4b-a9fc35d72b4d,Рада,EAN_13
|
||||
782f7353-ec4c-49a8-9aac-1f7d28f4cab2,Remix Moda,EAN_13
|
||||
783,Лукойл / Ликард,CODE_128
|
||||
@@ -6028,7 +5988,7 @@ _id,name,barcodeFormat
|
||||
7bd30784-434b-4d73-8dc1-5b5516723eda,Pascal Coste,EAN_13
|
||||
7bd61c87-b62d-439a-92e9-cc435345cb53,Infinity Fashion,CODE_39
|
||||
7c138f2e-37f9-46d4-ac65-2b20ff90a629,Nai Harn Gym,CODE_39
|
||||
7c1b39b5-b938-432e-b0be-3c196320bd37,Checkers,CODE_128
|
||||
7c1b39b5-b938-432e-b0be-3c196320bd37,Checkers,QR_CODE
|
||||
7c5a9dd0-28b0-4be1-b53f-cac4246990b4,Марафон Обувь,CODE_128
|
||||
7c60823a-e9fc-447f-811d-589bf1f95342,Пчёлка маркет,UPC_A
|
||||
7c77ce3b-02ad-436b-a4aa-62a6d5d583e3,Plainview-Old Bethpage Public Library,CODABAR
|
||||
@@ -6044,7 +6004,6 @@ _id,name,barcodeFormat
|
||||
7ce87cdb-4c6b-437f-a693-dca518f7436a,Yo-get-it,CODE_39
|
||||
7d02542c-fac0-45b5-bc90-d74240715c56,Travis Perkins,CODE_128
|
||||
7d11f040-b0a2-4109-bdf1-25711d48d451,Consorzio Infarmacia,EAN_13
|
||||
7d168ca5-9370-47bd-ac3e-bf1e1e26f1ec,RISPAWORLD,CODE_128
|
||||
7d41888d-cd7d-42ef-bf93-9aeda5ae13f6,Kepro,EAN_13
|
||||
7d4345b8-448b-4e12-a1c5-c6e031de2352,Nove25,CODE_128
|
||||
7d520d1c-611e-4e81-9937-41a9828e6b08,EU COVID-19 Vaccinatiebewijs,QR_CODE
|
||||
@@ -6055,7 +6014,6 @@ _id,name,barcodeFormat
|
||||
7da65ee3-d140-469c-b3ee-217272ac98d4,Kippie,QR_CODE
|
||||
7db0f727-13b4-48c1-8618-550155a878a2,Imperial Games,CODE_128
|
||||
7db8a067-1c33-4cd9-9706-31a2592f719a,милый дом,GS1_128
|
||||
7dd14421-2fe6-494f-889b-dd8920f61091,Mastro Tortello,QR_CODE
|
||||
7dd1b9ca-2a5b-4f3c-8c10-8bc216ff5d2f,Sokolov Jewelry,CODE_128
|
||||
7df2728d-3dc9-4724-8756-965e937674e2,Marriott Bonvoy,QR_CODE
|
||||
7e3da299-047b-4981-8ff3-e5355c7289b2,GIROPHARM ,EAN_13
|
||||
@@ -6082,7 +6040,6 @@ _id,name,barcodeFormat
|
||||
8045996b-082d-4333-b631-54dc992ebef0,Coop,EAN_13
|
||||
805,Старик Хоттабыч,CODE_128
|
||||
806,Stockmann,CODE_128
|
||||
8069f84c-3b04-4b0a-87fd-d89230547e8b,Happy Pets,QR_CODE
|
||||
807,Сток-центр,EAN_13
|
||||
8070cf0a-9721-4fe7-b010-6fdca61349fc,Epping Plaza Hotel,CODE_128
|
||||
8077e001-6db6-4796-bd82-6716ea5e116e,Palace Cinemas,CODE_39
|
||||
@@ -6104,7 +6061,6 @@ _id,name,barcodeFormat
|
||||
813f818a-e99d-49f2-af6e-653a9bcaab09,Bazar Avenue,EAN_13
|
||||
814,ФотоПлюс,CODE_128
|
||||
815,ЦентрОбувь,EAN_13
|
||||
8153abb1-248f-4af9-a7f8-dd83cdacdc7f,TEKBIR MARKET,CODE_128
|
||||
816,ЭКОНИКА,EAN_13
|
||||
8166ded7-42b6-47b8-a5dc-032954e82db7,bugatti,EAN_13
|
||||
817,Эстель Адони,EAN_13
|
||||
@@ -6115,7 +6071,7 @@ _id,name,barcodeFormat
|
||||
81c5ea7b-aa89-47f8-a22e-297207616f0b,Taurus Sports,CODE_128
|
||||
81dd0d8d-4613-400e-8cbd-b2189a88a22d,EULIVIA Apartments,CODE_128
|
||||
81e7b9b8-826c-4f9e-9c61-7568a454afa5,Industriya Krasoty,EAN_13
|
||||
82,Desigual,QR_CODE
|
||||
82,Desigual,CODE_39
|
||||
820,Air Miles,EAN_13
|
||||
820b5de7-a25a-4d30-ac74-3a70fe682bfd,Мир Электроники,CODE_128
|
||||
821,Ajax Amsterdam,CODE_128
|
||||
@@ -6179,7 +6135,6 @@ _id,name,barcodeFormat
|
||||
848,Lake Side,ITF
|
||||
848939e3-7e55-40af-a46a-a0b0b434bbcf,Планета ZOO,EAN_13
|
||||
849,Le Ballon,ITF
|
||||
8495d3db-8532-4bef-a58f-3a77479ff134,C&A,CODE_128
|
||||
84a82d8b-1d4f-4673-b1e2-b115bbe5b618,Soul Origin,CODE_128
|
||||
84faf272-0010-4f93-8aa1-154caaa11ac2,Pro-Duo Nur für Profis,EAN_8
|
||||
85,Diamond Club,CODE_128
|
||||
@@ -6239,7 +6194,6 @@ _id,name,barcodeFormat
|
||||
87737e38-8052-4fdc-a90a-3511b9157481,PETS&CO,CODE_128
|
||||
878,Jula,CODE_39
|
||||
879,KappAhl,CODE_128
|
||||
879a9dd3-45e3-4633-9376-9183fee6ab3e,Bernardi’s Marketplace,CODE_128
|
||||
87b3f071-9af7-4163-b512-679717b696ac,Caucciu,EAN_13
|
||||
87b925d1-4d9a-47e3-9e54-deaef1981b77,Impfausweis,QR_CODE
|
||||
87d141a6-cac3-4d39-9357-a6365850e57f,Coeur de frais,CODE_128
|
||||
@@ -6293,14 +6247,13 @@ _id,name,barcodeFormat
|
||||
8a0dca6e-de83-4e48-a42d-a3009da56653,Park 'N Fly,CODE_39
|
||||
8a25357e-ebc3-4ae1-b7fc-a10ff3b1abd0,Конфил,CODE_128
|
||||
8a53dffe-df27-40f0-b2ff-58e53add0b3e,La Cartissima,EAN_13
|
||||
8a59226e-9895-4924-8616-345549a56aec,Munhowen Drinx,CODE_128
|
||||
8a702666-368b-48a5-96fd-4e10aac5ae7f,Brooklyn Jeans,ITF
|
||||
8a8095fe-f449-4242-83a1-0d3055874233,Little Sparrow,CODE_128
|
||||
8a9c58f4-4db3-4aef-8cf0-d2caa0fcc4d1,EU COVID-19 Potrdilo o cepljenju,QR_CODE
|
||||
8aa58d48-ad60-4b6d-aa1d-054f94b6453b,Granola,PDF_417
|
||||
8ac5093b-8fc4-49d6-b271-dd845252b60c,Idea Verde Maschi,CODE_128
|
||||
8ad83ece-2e55-4937-80c9-04584c598439,COM,EAN_13
|
||||
8b0f2db1-ae97-4af8-8e82-c4067a4ac322,Ma Toyota Extra,CODE_128
|
||||
8b0f2db1-ae97-4af8-8e82-c4067a4ac322,Toyota,CODE_128
|
||||
8b398aea-e5bd-484d-bdf2-5030bacf9157,Thèoria Milano,CODE_128
|
||||
8b4c413c-effc-4912-9a34-6baea2972199,Karla,CODE_39
|
||||
8b653178-4f49-4f73-9091-7763e039b539,Aléa Déco,CODE_128
|
||||
@@ -6351,7 +6304,6 @@ _id,name,barcodeFormat
|
||||
903,W.KRUK,CODE_128
|
||||
904,Galeria Wileńska,UPC_A
|
||||
905,YES,EAN_13
|
||||
90574104-b485-489f-9872-3d32b7e07c59,America Today,CODE_128
|
||||
906,ZiKO Klub,EAN_13
|
||||
9062c2a3-eeb1-4797-afb6-41a0394bb481,Městská knihovna - Česká Třebová,EAN_13
|
||||
90705634-f152-487c-97eb-27e1728285ef,Миртек,EAN_13
|
||||
@@ -6380,7 +6332,6 @@ _id,name,barcodeFormat
|
||||
91915513-4447-47b0-93ae-d489f6ee3a97,Chrome,EAN_8
|
||||
92,Düsseldorf International,EAN_13
|
||||
920,Drummond Golf,CODE_39
|
||||
92063e91-526a-4327-ba87-f487bfaec724,Rue du Commerce,CODE_128
|
||||
920c9bd0-d85c-42c6-9301-fc1ddedd38c2,Idea Casa,CODE_128
|
||||
920ce49c-9728-41f1-b9e9-9f9d06f53d92,Русские Самоцветы,EAN_13
|
||||
921,NWZ,EAN_13
|
||||
@@ -6417,13 +6368,11 @@ _id,name,barcodeFormat
|
||||
935ef7c3-a93c-43e1-9abd-075bd05c3051,Форне,EAN_13
|
||||
936,Orlen - Vitay,CODE_128
|
||||
937,Wojas,EAN_13
|
||||
937cef67-4a01-42fc-9f51-0a3f3210a686,Idea Città Company,GS1_128
|
||||
938,Sizeer,CODE_128
|
||||
939,T2 Tea,CODE_128
|
||||
93a8cca4-73cd-405c-8142-359a41127416,しまむらグループ,CODE_128
|
||||
93a9836f-0984-45ee-97c6-3e6675a34b11,Ludwig Beck,QR_CODE
|
||||
93b76ad4-76f3-4132-8fe5-972f6ca5eb8a,Київфарм,EAN_13
|
||||
93bda8ac-884e-4db0-ab72-09e12f86a3d2,Naturino Family Store,CODE_39
|
||||
93c53a6b-2efb-4167-aa67-c4905f1692b1,ВелоДрайв,EAN_13
|
||||
93d1d2d1-801d-4293-a1f1-cdf314ba341a,Nilufar,EAN_13
|
||||
93d42408-df2a-42fd-a10c-9f9c725e8000,TuttintiMO,UPC_A
|
||||
@@ -6471,7 +6420,6 @@ _id,name,barcodeFormat
|
||||
962,Монро,EAN_13
|
||||
963,Jeans Symphony,EAN_13
|
||||
9630a33b-0869-4246-91db-80f928bd7b3a,Harfa Sport,EAN_13
|
||||
96394b6b-b91f-4fbd-991c-242b7189e0b0,Shoprite,CODE_128
|
||||
963a19ff-687c-434a-a960-c5e9c6d27c1c,La Cage,CODE_128
|
||||
964,Спектр,EAN_13
|
||||
964bee1b-84ac-42cb-ac20-b182e043a983,SIR,CODE_39
|
||||
@@ -6521,7 +6469,6 @@ _id,name,barcodeFormat
|
||||
989,Toys Center,EAN_13
|
||||
98959593-9b79-4d3a-98bf-fd965d99825e,ташир пицца,PDF_417
|
||||
98afc021-2350-4686-89de-03bc9bb686a4,Coeliac Australia,EAN_13
|
||||
98c597ea-20b1-4d9e-a6ae-0ed84e0f591d,Juttu,CODE_128
|
||||
98d5694e-ee5e-4f60-9a32-0ac43d66f54f,Vaprio,CODE_128
|
||||
99,Ernsting's Family,ITF
|
||||
990,Nando's,CODE_128
|
||||
@@ -6583,7 +6530,6 @@ _id,name,barcodeFormat
|
||||
9dc29233-9613-4851-8630-15b7b39222c3,Kasztelan,CODE_128
|
||||
9dc3174d-0990-4d88-a4d6-3c7a6431160d,Янтарь,EAN_13
|
||||
9dc63493-8062-498a-99be-db701dfc03a4,Farmacia,CODE_128
|
||||
9dd46ad3-336b-4af2-9cbc-4526140558ef,Kiriel,EAN_13
|
||||
9e02cf7a-da20-428d-a363-952f7a3fb25c,Kéddo,EAN_13
|
||||
9e82e20d-4da0-46c0-bb94-c2ba7b9b3d74,Индустрия красоты,EAN_13
|
||||
9ec73fed-0974-4b7c-98e0-27aba810e8e1,Spielwarentraum,CODE_128
|
||||
@@ -6595,7 +6541,6 @@ _id,name,barcodeFormat
|
||||
9fd0773f-f0ee-476c-8351-c02fb65b9360,Plus Market,EAN_13
|
||||
a00761f0-abf1-4690-a95a-b18e41c527d2,Pet and Pool,CODE_128
|
||||
a017f67b-3483-4587-97a0-2c5c4af6834e,SchuhMarke,CODE_128
|
||||
a0284158-4eaf-4891-9768-f93e1049413a,Десятка,EAN_13
|
||||
a04e9cdb-caec-4f4f-bf96-9e40fd90cb09,PharmaSave,CODE_128
|
||||
a05edd71-80dd-4e23-87cf-5df65a193281,Andre Tan,EAN_13
|
||||
a08ccd9d-76ce-4245-8582-24d2840ff7b9,Chanel,CODABAR
|
||||
@@ -6617,7 +6562,6 @@ a238f465-ff8e-4077-b5fe-a1f250ed90d9,BJ's Wholesale,UPC_A
|
||||
a2756aea-2ca4-4870-811e-100871fdb73e,Pratiko,EAN_13
|
||||
a29668f6-dd2e-4281-917e-49e28ebff6a1,Koloria,CODE_128
|
||||
a2b352d9-5d5d-4080-9f52-eb6a798aa6c6,Ferlenz,CODE_128
|
||||
a322cee9-b5c6-4384-a365-c970f335cc5c,Erdkorn,QR_CODE
|
||||
a323e0ec-2b0b-4a82-a950-11f7516f2584,OnePass,EAN_13
|
||||
a36556e0-433a-4b16-b72c-4751a386d707,EU COVID-19 Impfzertifikat - Erstimpfung,QR_CODE
|
||||
a3828047-ff01-4eb4-be10-6e4d635ca029,Leffers,ITF
|
||||
@@ -6646,7 +6590,6 @@ a6060858-7d83-4f60-8318-b80635013f45,Detershop,EAN_13
|
||||
a645973d-7e87-46ab-8c77-0380ca06ae32,Perth Zoo,CODE_39
|
||||
a65e3023-fa06-47c0-bfdc-4dc79f54c825,丁丁藥局,EAN_13
|
||||
a69154f5-16a8-4543-bb49-b7a68bb3d301,EU COVID-19 Potvrda o cijepljenju,QR_CODE
|
||||
a69d8b79-a0e7-422b-a149-64c66b23aea4,Plus More,CODE_128
|
||||
a6aa66ba-00b8-4922-b628-98cea029c9e2,Coop,EAN_13
|
||||
a6ab3df9-10bc-47df-bed4-839fe1e908be,零食物語,CODE_128
|
||||
a6b2c527-afbc-4e71-ae24-e5e5e270d474,Pappert,PDF_417
|
||||
@@ -6656,7 +6599,6 @@ a7634961-1509-4902-9b25-714ef789e926,2HB,EAN_13
|
||||
a78ee36a-3682-404f-9c83-307c1a6b421e,Moda Lina,EAN_13
|
||||
a79b9a92-9821-4824-978e-1a257abfbaff,Wormland,CODE_128
|
||||
a7b3e795-4746-45a4-9c80-d331fb051632,BonBon,EAN_13
|
||||
a7e263c3-75fd-4ac2-98ea-0e7b3e425a74,SUPEREFECTIVO,CODE_39
|
||||
a7f1c8c5-2895-4a74-98ac-9740e7c59922,Coffeelat,QR_CODE
|
||||
a8090907-7e2e-4038-8831-0c72adaa0664,US FashionStore,EAN_13
|
||||
a83b00dc-1bfd-41b6-9fee-3c7f5d33fef5,Baden,EAN_13
|
||||
@@ -6686,7 +6628,6 @@ aaa82398-d78f-46d6-bfb5-a40843e94cc8,CLEVER WEAR,EAN_13
|
||||
aabf2ea4-170c-42e4-906b-ea1253ebf580,Родные масла,EAN_13
|
||||
aac03de2-6c97-4bd9-8d72-a7bba15bea6d,La capsuleria,EAN_13
|
||||
aae4f87d-ee8c-4ff0-9cb2-88c478b7a0dc,Bonjour,EAN_13
|
||||
aae6aab3-e5fb-47c1-b6c1-c30c3f386793,Netto,CODE_128
|
||||
aaf65c10-a78e-4b18-8c79-371d5cdef871,La Provençale,CODE_39
|
||||
ab0c09c4-d1cc-40a4-8b46-f101dc376655,Trade group SMIT,CODE_128
|
||||
ab0c5857-5b3d-4ac3-8910-ec6b8c49a0dc,Three,EAN_13
|
||||
@@ -6695,7 +6636,6 @@ ab245924-7af0-4996-84a2-f19a6b6a62fa,Hollister,CODE_128
|
||||
ab37459c-4368-4684-9ffa-3ac84c69e87a,ДомДоктор,EAN_13
|
||||
ab4a36d9-9a11-4575-a6cb-1bd053c6e00f,СБА,CODE_128
|
||||
ab6de5de-ea68-47d6-87ad-884e63f63f48,EU COVID-19 Удостоверение за ваксинация - Първа ваксинация,QR_CODE
|
||||
ab73cd57-b075-425f-afe6-868e56207a42,Rewe,QR_CODE
|
||||
ab7a0e82-ad67-40fb-a85f-83cdd10fb44a,Depot,QR_CODE
|
||||
ab9d5459-25c3-4040-bff0-b7804375065f,Забіяка,CODE_128
|
||||
aba38815-1a55-456f-84b6-0321d8d34102,Андреич,EAN_13
|
||||
@@ -6736,13 +6676,11 @@ b000bec7-fe1d-4a01-8134-7e93c72fcf2c,фаэтон,EAN_13
|
||||
b00fc66a-460d-43c9-a5f1-86b0a92b125a,Дачник,CODE_128
|
||||
b0210273-794f-427b-bba1-c940a7aac7df,Helen,CODE_39
|
||||
b0382f02-57d7-4d7a-a3f1-25ea85507c64,Laser Game Evolution,CODE_128
|
||||
b059eafb-017b-49f0-9d74-62889d8ee777,City of Whitehorse,CODE_128
|
||||
b063caac-e875-4475-8ae6-09a0f979fb85,CLUB SALUTE,CODE_39
|
||||
b07244fc-81d3-492b-a9e5-a813a57eea9c,Faciba,EAN_8
|
||||
b07e5b4d-d658-4ba6-9305-d497af7a19ae,Nijhof Schoenen,ITF
|
||||
b086ef99-b8b8-45a9-80f5-33a4cb01aba8,spudshed,CODE_128
|
||||
b0973d67-75d0-45e3-9f17-0f4cb80a4824,Motozem,CODE_128
|
||||
b0cfcd52-01a5-4533-8970-6e402e52bcb0,Brikon,UPC_A
|
||||
b0e24b5a-4034-44b9-b22b-2a008d0bcde5,Eurodì,CODE_128
|
||||
b0efcdb1-872a-44f0-961a-a97ee45c7ba8,Porsche Group,QR_CODE
|
||||
b0f4291f-8d68-4071-8d10-cc212b4495cc,Iper d'Oriente,EAN_13
|
||||
@@ -6769,14 +6707,12 @@ b2ab5d25-1981-4120-be54-86ccda399861,Vitulano,UPC_A
|
||||
b2b50b52-83c6-43d3-bb13-008544e2cfa5,Turčianska knižnica,EAN_13
|
||||
b2b7d24b-fdbc-468b-be59-b189d4d5fdf9,Het Certificaat B.V.,QR_CODE
|
||||
b2c03313-9621-4233-9b61-5faa8d2c66e0,JILL STUART,EAN_13
|
||||
b2e520a4-c21a-4ba0-822b-c9ac5fe79f4d,BLUME2000,QR_CODE
|
||||
b2f90e3a-4669-4cd4-8c31-65fbb91dc26e,Advantage Pharmacy,CODE_128
|
||||
b31982e9-7c22-4e92-8210-e08eaa123727,Linberg,EAN_13
|
||||
b334927e-9574-457c-9a1f-1b7dd5928304,Farmanoi,CODE_128
|
||||
b359db35-9be6-4369-b796-04b47b4044be,Signorizza,EAN_13
|
||||
b36ae43e-8a9c-41f7-8c54-d5ae673c94f5,Bio&Co,EAN_13
|
||||
b43d0b6b-db53-44a7-b518-30cace59c222,British Garden Centres,CODE_39
|
||||
b4606b36-853e-4014-9524-fc07fa6e1d4a,Cantina Rauscedo,EAN_13
|
||||
b4663d4f-dd9f-43cc-ba0e-4ce9b0beccd2,Пивлавка,EAN_13
|
||||
b4725b6c-105f-4898-a8d5-ba426ddf9508,Yamazaki,CODE_128
|
||||
b472df21-8f40-44ff-a11f-bbe1d76d6d58,Company Shop Group,CODE_39
|
||||
@@ -6784,11 +6720,9 @@ b497667e-0c92-4db6-9579-63bbe35af881,Праздничный,QR_CODE
|
||||
b4b5583a-3d0e-458e-b800-3b43968a8421,Pirex,CODE_128
|
||||
b4c412d7-ad0b-4afd-aed8-0cf113f445ca,Аквафор,CODE_128
|
||||
b4e4e61f-8605-45b6-b672-fce67898ba4e,Schuhkay,EAN_8
|
||||
b4f37441-b068-443f-bbfb-fca23c9f5eec,Tuttigiorni,EAN_13
|
||||
b4f4c3c3-4ad3-4431-9048-1d6b0e47a649,Tezenis,CODE_128
|
||||
b52836be-a999-4bf8-ba0b-5f2b9b96a509,Youth Hostels Luxembourg,CODE_128
|
||||
b54963ea-a217-434b-b0fa-e8114fd6b999,Пинта,EAN_13
|
||||
b54ed01d-e46b-4f24-8ce9-e08f624f2ddb,IGA,CODE_128
|
||||
b5656988-55fb-46c8-91ab-24a5b8422549,Moja Starówka,CODE_128
|
||||
b5695b84-a5cf-4286-87ab-afbe9368be1f,Tulipes,CODE_39
|
||||
b5dc4188-75d6-4cf1-b7f2-b0e85a57bc9a,Boulangeries Maison Toulorge,CODE_128
|
||||
@@ -6823,7 +6757,6 @@ b9c4e2bd-88ee-4345-b0c4-3828e076637c,Pro-Duo Exclusief Professionals,EAN_8
|
||||
b9f36613-ed74-441e-abce-66d465b83594,Accademia Italiana della Cucina,CODE_39
|
||||
b9f3eacc-e6d9-43e2-93f0-a1e63221b1fe,Più Medical,CODE_128
|
||||
b9fc9d9a-da0e-4fe2-82d8-5d6672263b4b,Kačka,CODE_128
|
||||
ba063e76-f5be-4e98-a549-7040a825caf7,Trendevice,CODE_128
|
||||
ba0d23c2-0030-4b68-9bec-6daf6c0db596,Zoomarket,CODE_128
|
||||
ba119be5-7382-453c-93be-625c555aec84,Vitaminas,CODE_128
|
||||
ba5aca20-b0fd-417d-8739-ba9b347c8fff,Клиника ЛМС,CODE_128
|
||||
@@ -6889,7 +6822,6 @@ bfbe8661-ae7a-4338-bb37-fde8cd6c57a1,Хмель и Солод,EAN_13
|
||||
bfcd1bbc-3671-4a2b-99d4-8195c5246644,Metalmark,EAN_13
|
||||
bfe5aac8-ea2d-41e0-ba15-af949e5437d7,Каприз,EAN_13
|
||||
bff24292-b2e3-4322-9462-d5ecc80ce044,Halfords Motoring Club,QR_CODE
|
||||
bricoman-it,Bricoman,CODE_128
|
||||
c03f0f47-ce09-4bf1-95f8-c1d0c6f1a8ca,Coop,EAN_13
|
||||
c043ef0e-49a9-4f10-877f-974247cf0f16,IperBiobottega,EAN_13
|
||||
c0712c54-a6a6-4695-b9ba-4f5a296b66cf,Apothical,EAN_13
|
||||
@@ -6939,7 +6871,6 @@ c51b31d2-056b-41a0-9347-c4d02375df01,офисмаг,EAN_13
|
||||
c51c692c-9e90-48fb-9047-38d3bb7fec2d,Мясницкий Ряд,CODE_128
|
||||
c53f804f-29e6-4dc0-9f66-0b9b016cdade,Möbel Borst,CODE_39
|
||||
c54a0027-fd79-457e-80eb-e73e1332e3e9,Ni Hao,CODE_128
|
||||
c57001e2-db2b-4f15-8c49-29c6502a86e8,Underwood Meat Company,CODE_39
|
||||
c5846a8f-687a-4de9-a5b5-b575488ac84b,Radhe Wholesale & Retail,EAN_13
|
||||
c59fc214-7895-40fc-8f94-9d1d800b66d2,Conradt,CODE_39
|
||||
c5acc06c-0b7d-4e4d-bee3-2134e2fb3b9c,Belles Fleurs,EAN_13
|
||||
@@ -6979,7 +6910,6 @@ c9231cc7-92f2-447c-ad84-8d167c23e9cd,Zwitserse Apotheek,CODE_128
|
||||
c925f293-54ee-47ba-ba48-792945c5fa94,Смайл,UPC_A
|
||||
c9295edb-4acf-4e21-b931-d07d1b97e9be,Weingärtner Gartencenter,EAN_13
|
||||
c935a5b9-03f1-4194-8aa2-39545b376065,Alpina Intimo,CODE_39
|
||||
c94a90ff-4118-4310-bcf2-588463110b83,knihovna Rosice,CODE_128
|
||||
c964ff0f-5ac9-4976-967f-a55c7ec72e14,Mega Pet Warehouse,CODE_128
|
||||
c998f7d2-6403-46c5-ba21-270195e61cd3,MAX & Co,EAN_13
|
||||
c9d387cb-7a0f-492f-a18d-f4d559ccbade,Информат,EAN_13
|
||||
@@ -6991,13 +6921,10 @@ ca4944a1-3892-4803-8b04-b72cd996511f,Diadema,CODE_128
|
||||
ca650de4-55cc-4df6-8994-3378274bebf5,Moby Dick,CODE_128
|
||||
caa55951-513c-4dca-b0bc-3cb80d85e4f2,PANORAMICO,EAN_13
|
||||
cab2ae0e-10bc-4c58-b159-59f4e8566ca7,Hawkesbury Library Service,CODE_39
|
||||
cad853d8-b9fa-43d6-b37d-39274a571269,Harmony Beauty,EAN_8
|
||||
caddfc56-1d2a-454c-bece-1516b13fa249,Millstream,EAN_13
|
||||
cae4d233-caae-43ff-aaba-affdc99c2d98,ALTERNATURA d.o.o.,EAN_13
|
||||
cae69560-d7e6-4cb7-9ac5-95199c15f9cc,Blumenmarkt Dietrich,CODE_128
|
||||
caff4297-2ae6-4315-9329-614c8510eb7f,Вместе Выгодно,CODE_128
|
||||
cb03988e-5063-4f48-aef2-9f959f9771a2,DVV,CODE_128
|
||||
cb12d304-17dc-45ba-be1c-5602237320ce,Vero Moda,QR_CODE
|
||||
cb1f1114-d1ea-4987-badc-7194d1ab1ca8,Zahradní Centrum,CODE_128
|
||||
cb4ead90-a2f7-41ba-80eb-d4970bed83bd,A-Kaart,CODABAR
|
||||
cb7b9237-0c2d-437a-ba38-fa6decca977e,萊爾富,CODE_128
|
||||
@@ -7020,7 +6947,6 @@ cd121cb8-988c-454f-a4ac-10365bf4aa6c,Shop Santé,CODE_128
|
||||
cd26930f-c1ac-4543-a23c-0b90cfa0b1f7,36.6 Здоровье,EAN_13
|
||||
cd38f71a-1a0a-4ba7-ac1d-43974fd42e1a,Gel Market,EAN_13
|
||||
cd73cbfb-68f5-4d67-9411-310695558c6b,NKC,CODE_128
|
||||
cd840f28-f17c-44ed-9ec7-15b48aa2f0e1,Knihovna Matěje Josefa Sychry,EAN_13
|
||||
cd9d6482-a7dd-4283-a776-f0982ade57a5,Biraghi,EAN_13
|
||||
cdd777ae-6fa4-458d-b7e5-f7c18fff857a,EU COVID-19 Vaccinationsintyg,QR_CODE
|
||||
cdd87d70-3e73-48a2-a88a-5e1083e41d0a,1000 мелочей,EAN_13
|
||||
@@ -7042,9 +6968,7 @@ cf4f5874-aef4-492c-ae9c-b47cb2f14224,Jardinerie Loiseau,EAN_13
|
||||
cfce4667-ff5d-44f0-8ba7-fbc44bbf2cb5,Orange Club,CODE_128
|
||||
cfd15fb5-1bac-455b-a5f7-b808390fba06,Сакура Суши,EAN_8
|
||||
cff8ca3d-3620-4098-9b8b-e181f84f6ec8,365,CODE_128
|
||||
d0153291-afc6-4d0f-8120-74c0b321434a,SA Guild of Actors,CODE_128
|
||||
d0540b51-9716-4d59-bc2f-1582b044c029,Wedding Price Card,CODE_128
|
||||
d05b520c-091a-4a9b-84de-689484927109,Lotto Niedersachsen,DATA_MATRIX
|
||||
d0a04b4f-df54-4fcd-b410-87ea5d0986aa,EU COVID-19 Očkovací preukaz - Záber na prvé očkovanie,QR_CODE
|
||||
d0b9a6b8-f724-4fe7-8195-e810297505af,Chocolaterie Albèrt,EAN_13
|
||||
d1018675-b1b2-44bc-91b6-a985d744836f,La Sirena,EAN_13
|
||||
@@ -7080,7 +7004,6 @@ d403852e-7683-49f0-9de5-6e1ec5ac842d,Andreas,CODE_128
|
||||
d4115422-7d2e-4001-9c49-4c1353c8b88d,Secom,EAN_13
|
||||
d44c1355-2941-4393-aeb8-1a7ad7122f67,HUALI MARKET,EAN_13
|
||||
d4502068-af6b-43ab-b9a5-46dc1899e22a,Ябко,EAN_13
|
||||
d4517693-3f1c-45a6-86f2-d60ad19d04e9,U Baristu,QR_CODE
|
||||
d4934c41-3cae-40dd-bd5c-2ca88bdcf9f5,Bau-Buy,EAN_8
|
||||
d4b67cb7-cfbf-4bac-8711-2088b8592e5f,Wara,EAN_13
|
||||
d4e44512-0ac2-4d1f-8603-01cd0497416c,The co-operative,CODE_128
|
||||
@@ -7111,7 +7034,6 @@ d6eb202f-ba2f-4253-8f5d-1dce44d13bef,Канцлер,EAN_13
|
||||
d71e4888-dd0b-4aac-ae5b-937b17ee4149,FQCC,EAN_13
|
||||
d7893d3c-c704-4daa-955b-a97f061d0138,ВАБИ САБИ,CODE_128
|
||||
d78fc335-cab2-40d7-a56c-333f568b36b4,социалочка,EAN_13
|
||||
d7959c14-98b1-4187-9088-494d1a7c5f9f,Canningvale,CODE_128
|
||||
d79a1500-206d-407a-b111-724b898aa154,Sportsman's Warehouse,CODE_39
|
||||
d7a18a8f-32b5-43f5-8290-5caf4297aaf8,Halfords Colleague Discount,CODE_128
|
||||
d7b8deb4-4006-4223-9600-331458fade3d,Пиватерра,EAN_13
|
||||
@@ -7178,7 +7100,6 @@ dema-be,DEMA,EAN_13
|
||||
df2f73ec-a3c1-4169-b47e-4742bcab704d,Digizenz,QR_CODE
|
||||
df3228e8-78d0-42c7-8e45-30089e5267ea,Эдисон,EAN_13
|
||||
df53a52a-320b-41ce-8ca0-92da86fcae0c,Koutný spol,CODE_93
|
||||
df5ad302-ae2d-47db-b9c9-b5e030d3b553,ALDI,CODE_128
|
||||
df62dc4f-b31a-4615-a289-94410da0ce7b,Melkior,CODE_128
|
||||
df668825-ed7c-4f05-b74b-47ec6daa69f0,Breakers,CODE_39
|
||||
dfc5ba69-483e-46ab-8951-3afc7c6d7460,Chaussexpo,CODE_128
|
||||
@@ -7191,7 +7112,6 @@ e0491f99-5f5b-4bfa-bb1d-f7cfe688ca26,Хмельная Миля,CODE_128
|
||||
e0663514-cb9c-413a-ad94-8b83dde796f8,Hommy,EAN_13
|
||||
e0b022eb-bc2b-4553-8345-5869e4f644e2,Life 2.0,CODE_39
|
||||
e0b2fcbb-e302-4a5e-aa4b-3991fcee7831,KanclerCom,EAN_13
|
||||
e0d0863f-c345-4e3d-baf7-853414056795,Sport 2000,EAN_13
|
||||
e0db8778-d9a2-4b6c-bece-1b2c4bef11c0,Everyone Fitness,CODE_39
|
||||
e0eadec9-539e-4316-b9bd-9e29d59c1abb,Containers for change,CODE_128
|
||||
e132948b-f6a2-44cb-b0c1-d9366151a0e2, BSTRONG,CODE_128
|
||||
@@ -7226,13 +7146,11 @@ e435e3ee-a81f-40f8-86be-2def0a610ac1,Спорт-Марафон,EAN_13
|
||||
e4561f48-5c68-4c2e-88ea-7eeb531a8b41,Lubo,CODE_128
|
||||
e456ceeb-d76a-4684-9e2a-54935e77daa5,Tendenze Calzature,EAN_13
|
||||
e4dfacd9-9513-4231-b09b-51af53151edd,Дворик,EAN_13
|
||||
e4f5270b-5a69-41a3-a39e-e3e7e4460ddd,OSCARwash,QR_CODE
|
||||
e4f54b47-0238-4fd6-9109-d5ce424981c6,Фламинго,EAN_13
|
||||
e5059f27-dc93-4296-b4d5-1162b692c5ec,Северная Звезда,CODE_128
|
||||
e550a9a1-c25b-4658-a9fa-38764c584693,Mon Grand Plaisir,QR_CODE
|
||||
e55b3ee0-ac34-480c-8fd3-c63c3a6ae28c,Муниципальная Аптека,EAN_13
|
||||
e55f98ef-9258-4eb7-97fb-7e97d2aacdaa,COOK Kitchen,QR_CODE
|
||||
e5616ded-48e7-45d7-b706-a82ef5ab9667,OROCASH,CODE_39
|
||||
e569e534-de02-4cde-a15e-ee5f3e70794e,Partyland,CODE_128
|
||||
e570f1ac-a109-4473-8644-9b6daf701d8d,najlepšia lekáreň,CODE_128
|
||||
e580263e-726d-4768-a756-1cec4966dbb6,Lower Plenty Hotel,CODE_128
|
||||
@@ -7245,12 +7163,10 @@ e6b0d8c0-2e2b-4d2c-9c3d-4420ced94877,Багира,EAN_13
|
||||
e6b4a59b-4d9a-42c6-aae3-5baf468c1999,Evolution,EAN_13
|
||||
e6c68ae5-12f0-4c8b-b5ca-8f725874c704,Полушка,EAN_13
|
||||
e6e830c8-16b9-4382-9b84-93dca76ee66c,домаркет,CODE_128
|
||||
e6ece7bc-ac39-45c6-b4f3-c225719c3a0e,Mikado,CODE_128
|
||||
e6edbb92-d988-4bf3-87f8-e9684b5a3983,UFS Healthcare,CODE_39
|
||||
e6edbb92-d988-4bf3-87f8-e9684b5a3983,UFS Dispensaries,CODE_39
|
||||
e6efc01d-98bf-478e-a916-f51178a01690,Erborian,CODE_128
|
||||
e6f32c21-af1b-4da3-9c8e-36757cccde3b,Sally Beauty,CODE_128
|
||||
e6f9e7a3-2b1f-4ec7-8c99-8c5d16988f56,Iндустрiя краси,EAN_13
|
||||
e71a67d2-6898-4a05-91dd-7ae19095129f,FMBrikon,UPC_A
|
||||
e71b01e0-cdf1-4f6b-bee6-d7e2fc9b3a81,Walder Schuhe,CODE_128
|
||||
e760dd3f-aeb2-42a2-bf38-5866c061c2e9,Cash Piscines,CODE_128
|
||||
e79c474b-4ee0-4885-a9eb-7349bdc2bfc9,KIA,CODE_39
|
||||
@@ -7284,7 +7200,6 @@ eaacfd6c-54dd-4bbd-81a2-0394b7b57496,Kmart,EAN_13
|
||||
eab09679-f885-46a1-8f96-3f82ea3b9d82,Niké,ITF
|
||||
eac387cc-ae67-4874-b420-12dae0150abc,Woss,EAN_13
|
||||
eacb1c97-e7c2-4ed6-bf64-84db244fbdd5,Медтехника Ортосалон,EAN_13
|
||||
eacdf92e-6601-437d-af01-15156a3ee199,Barossa Co-op,QR_CODE
|
||||
eb01f161-6d42-4ae9-b381-2ca0be34cd6f,PiùMe,CODE_128
|
||||
eb2cfbfc-1d25-4ff7-9eb6-743a74c302c4,Клеопатра,EAN_13
|
||||
eb32c9d7-80b8-4147-942f-3b94ad7dd8fd,Brico Pro,EAN_13
|
||||
@@ -7338,7 +7253,6 @@ ef56f2fe-b4b0-4639-a0dc-db4c6bd01d06,7я,QR_CODE
|
||||
ef8b1a62-353b-44e3-bfba-b1331b6509ab,Evoluphar,CODE_128
|
||||
ef8f92d7-a5a1-441e-8e91-133b64da57e5,Anabel Arto,CODE_128
|
||||
efdfda06-b4ad-4bd6-ad00-41d6ab9aeaf8,Profi Center,CODE_128
|
||||
effbec31-0ed6-4eb3-969b-17d99d340d78, Sedici Piadina,CODE_128
|
||||
f01c0047-5952-4805-a48b-4d455d833777,ХозСити,EAN_13
|
||||
f032c0d2-9f71-47fa-9574-8970a917b63b,Brianza Biblioteche,EAN_13
|
||||
f0637a9d-47a8-44a0-8342-c409b6c55b6b,Baby,EAN_13
|
||||
@@ -7355,7 +7269,6 @@ f1843eba-2bcd-49dc-be2c-1444ff5cfd91,EU COVID-19 Očkovací Průkaz,QR_CODE
|
||||
f1df75b9-1d7a-4cba-9e9d-f4411f4ea48b,Индейкин Дом,EAN_13
|
||||
f1e508d1-b901-45ba-9ace-b98e96c8fd38,Dalbe,EAN_13
|
||||
f1f1c15f-8a75-4a18-9b01-251778c8fb45,Optika Anda,CODE_128
|
||||
f1fe28ce-0c9a-4b64-a455-c9f14c3fa2be,PME Legend,CODE_128
|
||||
f2153289-2b50-463f-91d4-37ceb62f304b,Колесо2,CODE_39
|
||||
f21a2eea-3a15-4765-8ea6-3f1ec10fdd87,EU COVID-19 Vaccinationsattest - Anden vaccination Skudt,QR_CODE
|
||||
f2292778-e0fe-4925-b939-b4716342fa44,Tread & Miller,CODE_128
|
||||
@@ -7370,10 +7283,8 @@ f2a92584-5ef8-4220-b0ca-7aa48decd2e4,Artex Ieper,EAN_13
|
||||
f2b9fa76-c78f-4d2c-821f-70678bc8d4d5,Parfümerie Becker,EAN_8
|
||||
f2c8f722-9c5f-423d-9989-deca7901aa11,Poetry,CODE_128
|
||||
f2d3f68c-7b77-4464-91d2-3162e74bea48,Neinver,EAN_13
|
||||
f2dc6f84-01cc-4e13-aec2-2ce88367a27f,Ljekarne Prima Pharme,CODE_128
|
||||
f3189d64-dd39-468b-872d-3bb70e4d416c,The Watergardens Hotel ,CODE_128
|
||||
f3287ab2-0308-42f8-92dc-3147456a4a69,НУЖНО!,EAN_13
|
||||
f359407e-234b-4fbb-af07-f3b293a51bbb,MaRinella,EAN_13
|
||||
f35a3882-27b2-417d-8093-e87f8f25509a,Первый Семейный,CODE_128
|
||||
f3852d29-47fe-4528-83cd-5ae7b31fdb0e,Kraus,PDF_417
|
||||
f3e63893-802b-4e40-9480-f3fbfda0a3e4,Аптека живика,EAN_13
|
||||
@@ -7386,7 +7297,6 @@ f49e49df-1b1c-4e19-994d-3a56c693d91c,GROSBASKET,EAN_13
|
||||
f4aefdf7-e66f-4980-a0ee-7e6f1afcc8df,Color Line,EAN_13
|
||||
f4b16522-478d-4c84-bfa5-e0825ebf4917,bonVito,PDF_417
|
||||
f4d0cac3-70a0-43dc-a204-fe5fd9ab428f,KüstenCard Flexi,CODE_128
|
||||
f4e09fa3-b712-4be5-915b-002082002246,Club VW Suisse,QR_CODE
|
||||
f5002bd9-8e95-4c11-8a7c-e3d2fae42fe3,BCAA,CODE_128
|
||||
f5356dd8-8762-4f36-8c50-f7383eccb840,Twój Market,EAN_13
|
||||
f546e937-86b4-40eb-98cb-9a348d5dccec,МаксиФлора,EAN_13
|
||||
@@ -7422,7 +7332,6 @@ f8f0bd64-d1ae-4560-9c22-0eed805f2016,Дивный Колибри,EAN_8
|
||||
f8fa2370-261e-4e19-ba9c-46cd33ead64d,Agri Sud Est,EAN_13
|
||||
f90691bf-2879-4424-b2d5-5c09ee9ff700,Кроха,CODE_128
|
||||
f915ed01-85f9-4a61-921b-0d33eaf6fd23,ЗооОптТорг.Рф,EAN_13
|
||||
f9223231-26b6-4f86-9d2e-5756488c2e74,Jack & Jones,QR_CODE
|
||||
f93e7a30-4351-47e5-b8b2-3a9546ad9bb8,BOTICINAL POWERSANTÉ,EAN_13
|
||||
f940a1b8-c04b-4541-b307-7fdc1fa8eb91,Veggie Grill,CODE_128
|
||||
f9447f67-140e-402d-9a27-e7c11cefebda,Eleganza,CODE_128
|
||||
@@ -7441,7 +7350,6 @@ fa009005-250b-4994-a6ad-8043b28634fe,No One,CODE_128
|
||||
fa11b2c7-a768-4d4b-b03d-c845df6cb341,Terra Viva,CODE_128
|
||||
fa1670c0-1713-44f0-b57d-902b278ba741,нива,EAN_13
|
||||
fa24b789-4774-41e1-8a52-216efc9de8ba,foodmaster,QR_CODE
|
||||
fa3bdecd-2216-4d2b-b39d-fb14681f62fc,Fusion Gyms,CODE_128
|
||||
fa5593eb-2f35-4a7f-8c69-1c4a726759be,Форум,EAN_13
|
||||
fa7407ee-0ddd-4727-bfc7-05c206c159d0,Toto,EAN_13
|
||||
fa7f3968-0cba-4adb-b1bb-fb2083b98b2f,Der Bäcker Eifler,QR_CODE
|
||||
@@ -7452,7 +7360,6 @@ fac3cc98-d990-4106-b17a-e8b5afe1b843,Fidenza Village,CODE_128
|
||||
fadd868f-b34b-4604-8a24-c7fbcd8ea573,Big Marlin,EAN_13
|
||||
fae896a0-9c57-4ff8-be30-195fbf137a0b,Lotteria degli Scontrini,CODE_128
|
||||
fafa23c9-5cda-4fb8-aab5-6faebc6386a8,NETTO,CODE_128
|
||||
fb340faf-4fe5-4446-b811-217d615f5514, Abbonamento Musei,QR_CODE
|
||||
fb507b68-ecf4-4397-969a-23e2427f76f2,Veritas,EAN_13
|
||||
fb5e84a1-5e9f-4fa5-ad36-c6060927c415,BIT BY BIT,CODE_128
|
||||
fb6edc61-a282-4217-9b44-ac2611b5977c,Kierrätyskeskus,CODE_128
|
||||
@@ -7493,10 +7400,8 @@ fe488a32-17aa-4b93-8e88-b2df166b30b8,BIEMAR BOIS,CODE_39
|
||||
fe54303c-8e1c-4c62-8ee6-b9485e333419,Liverpool Library,CODE_128
|
||||
fe889ad0-ea52-4069-a051-b5ceb4c4b4e7,Аптека Гермес,EAN_13
|
||||
febc239e-ed07-45ac-905d-b6048a203784,Scarpamondo,EAN_13
|
||||
fed489b7-1d23-4b3f-b20f-52c229575de0,Autowaspark Kuzee,QR_CODE
|
||||
fee32f93-2fe4-4fa1-ab62-159bdc375668,Покупочка,CODE_128
|
||||
fefcdd70-4aa8-4f78-b9e6-1dc18f9cd731,Button Blue,EAN_13
|
||||
ff50e5dc-1f3a-43a7-a55d-4a7d96b12757,Le Guidon Niortais,CODE_128
|
||||
ff92fe3e-1b38-409f-9701-ee7665fccb5e,EU COVID-19 Certificado de Vacinação - Primeira injeção,QR_CODE
|
||||
ff9fd337-4765-4ad1-90a3-62e4a78dc3ec,Нияма,QR_CODE
|
||||
ffa57152-01bd-48bc-be45-46bac303c450,Мед Сервис,CODE_128
|
||||
|
||||
|
@@ -50,11 +50,11 @@
|
||||
<string name="settings">اعدادات</string>
|
||||
<string name="settings_light_theme">فاتح</string>
|
||||
<string name="settings_dark_theme">داكن</string>
|
||||
<string name="settings_card_orientation">اتجاه الشاشة</string>
|
||||
<string name="settings_card_orientation">اتجاه الباركود</string>
|
||||
<string name="settings_portrait_orientation">الوضع الرأسي</string>
|
||||
<string name="settings_landscape_orientation">الوضع الأفقي</string>
|
||||
<string name="settings_theme">مظهر</string>
|
||||
<string name="settings_display_barcode_max_brightness">عرض مشرق علي الشاشة</string>
|
||||
<string name="settings_display_barcode_max_brightness">عرض مشرق علي الباركود</string>
|
||||
<string name="importSuccessful">تم استيراد البيانات</string>
|
||||
<string name="exportSuccessful">تم تصدير البيانات</string>
|
||||
<string name="enter_group_name">أدخل اسم المجموعة</string>
|
||||
@@ -119,6 +119,7 @@
|
||||
<string name="settings_blue_theme">أزرق</string>
|
||||
<string name="settings_sky_blue_theme">أزرق سماوي</string>
|
||||
<string name="settings_green_theme">أخضر</string>
|
||||
<string name="settings_grey_theme">رمادي</string>
|
||||
<string name="settings_brown_theme">بني</string>
|
||||
<string name="app_contributors">أصبح ممكنًا بواسطة: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="sort">فرز</string>
|
||||
@@ -237,6 +238,9 @@
|
||||
<string name="importLoyaltyCardKeychainMessage">حدد ملفك <i>LoyaltyCardKeychain.csv</i> التصدير من Loyalty Card Keychain للاستيراد.
|
||||
\nقم بإنشائه من قائمة الاستيراد / التصدير في Loyalty Card Keychain بالضغط على تصدير هناك أولاً.</string>
|
||||
<string name="importStocard">الاستيراد من Stocard</string>
|
||||
<string name="privacy_policy_popup_text">إشعار سياسة الخصوصية (مطلوب من قبل بعض متاجر التطبيقات):
|
||||
\n
|
||||
\nلا يتم جمع أي بيانات على الإطلاق ، والتي يمكن لأي شخص تأكيدها لأن تطبيقنا هو برنامج حر.</string>
|
||||
<string name="failedGeneratingShareURL">تعذر إنشاء عنوان URL قابل للمشاركة. الرجاء الإبلاغ عن هذا.</string>
|
||||
<string name="help_translate_this_app">ساعد في ترجمة هذا التطبيق</string>
|
||||
<string name="on_google_play">على Google Play</string>
|
||||
@@ -247,15 +251,23 @@
|
||||
<string name="barcodeLongPressMessage">يمكن فتح صور فقط في تطبيق معرض الصور</string>
|
||||
<string name="failedToOpenUrl">ثبت متصفح ويب أولاً</string>
|
||||
<string name="welcome">مرحبا بك في كاتيما</string>
|
||||
<string name="updateBalanceTitle">كم أنفقت أو استلمت؟</string>
|
||||
<string name="updateBalanceTitle">كم أنفقت؟</string>
|
||||
<string name="currentBalanceSentence">الرصيد الحالي: <xliff:g> %s </xliff:g></string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="zero">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقة)</item>
|
||||
<item quantity="one">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقة)</item>
|
||||
<item quantity="two">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقتين)</item>
|
||||
<item quantity="few">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقات)</item>
|
||||
<item quantity="many">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقات)</item>
|
||||
<item quantity="other">عرض الأرشيف (<xliff:g>%1$d</xliff:g> بطاقات)</item>
|
||||
</plurals>
|
||||
<string name="importCards">استيراد البطاقات</string>
|
||||
<string name="newBalanceSentence">الرصيد الجديد: <xliff:g>%s</xliff:g></string>
|
||||
<string name="cameraPermissionDeniedTitle">تعذر الوصول إلى الكاميرا</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">لمسح الباركود، ستحتاج Catima إلى الوصول إلى الكاميرا. اضغط هنا لتغيير إعدادات الأذونات.</string>
|
||||
<string name="updateBalance">تحديث الرصيد</string>
|
||||
<string name="updateBalanceHint">أدخل المبلغ</string>
|
||||
<string name="storageReadPermissionRequired">الصلاحيه للوصل للتخزين مطلوبة لهذا الاجراء…</string>
|
||||
<string name="storageReadPermissionRequired">الصلاحيه للوصل للتخزين مطلوبة لهذا الاجراء</string>
|
||||
<string name="validFromDate">عربيه</string>
|
||||
<string name="cameraPermissionRequired">إذن للوصول إلى الكاميرا اللازمة لهذا الإجراء…</string>
|
||||
<string name="anyDate">أي تاريخ</string>
|
||||
@@ -277,7 +289,7 @@
|
||||
<string name="action_display_options">خيارات العرض</string>
|
||||
<string name="settings_oled_dark_summary">يقلل من استخدام البطارية على شاشات OLED</string>
|
||||
<string name="icon_header_click_text">اضغط لفترة طويلة لتحرير الصورة المصغرة</string>
|
||||
<string name="settings_category_title_cards">البطاقات الظاهرة</string>
|
||||
<string name="settings_category_title_cards">البطاقات</string>
|
||||
<string name="show_note">إظهار الملاحظة</string>
|
||||
<string name="switchToBackImage">التبديل إلى الصورة الخلفية</string>
|
||||
<string name="switchToFrontImage">التبديل إلى الصورة الأمامية</string>
|
||||
@@ -298,38 +310,6 @@
|
||||
<string name="enter_card_id">أدخل رقم الهوية أو النص الموجود على بطاقتك</string>
|
||||
<string name="addWithoutBarcode">إضافة بدون باركود</string>
|
||||
<string name="field_must_not_be_empty">يجب ألا يكون الحقل فارغا</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="app_name">كاتيما</string>
|
||||
<string name="settings_follow_sensor_orientation">التدوير دائمًا ( تجاهل إعدادات النظام)</string>
|
||||
<string name="add_manually_warning_title">الفحص موصى به</string>
|
||||
<string name="continue_">استمر</string>
|
||||
<string name="spend">انفق</string>
|
||||
<string name="receive">استلم</string>
|
||||
<string name="amountParsingFailed">كمية غير صحيحة</string>
|
||||
<string name="add_manually_warning_message">في بعض المتاجر قيمة الباركود تختلف عن الرقم الموجود على البطاقة. لهذا السبب إدخال الباركود يدوياً لن ينجح دائماً. من المستحسن فحص الباركود بكاميرا بدلا من ذالك. هل انت مُصِر على الاستكمال؟</string>
|
||||
<string name="addFromPdfFile">تحديد ملف PDF</string>
|
||||
<string name="errorReadingFile">لا يمكن قراءة الملف</string>
|
||||
<string name="failedLaunchingFileManager">لم يتم العثور على مدير ملفات مدعوم</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">اي من الـbarcodes تريد استخدامه؟</string>
|
||||
<string name="pageWithNumber">صفحة <xliff:g>%d</xliff:g></string>
|
||||
<string name="noCameraFoundGuideText">يبدوا أن جهازك لا يمتلك كاميرا. إذا كان يمتلكها، أطفئ الجهاز وحاول مرة اخرى. اذا لم ينجح ذلك، أضغط على زر \"المزيد من الأختيارات\" في الأسفل لإضافة الباركود بطريقة أخرى.</string>
|
||||
<string name="importCancelled">تم الغاء الاستيراد</string>
|
||||
<string name="exportCancelled">تم الغاء الاستخراج</string>
|
||||
<string name="useFrontImage">استخدام صورة أمامية</string>
|
||||
<string name="useBackImage">استخدم صورة خلفية</string>
|
||||
<string name="addFromPkpass">اختر ملف الدفتر(.pkpass)</string>
|
||||
<string name="unsupportedFile">هذا الملف غير مدعوم</string>
|
||||
<string name="generic_error_please_retry">نعتذر، حدث خطأ ما، حاول مرة أخرى...</string>
|
||||
<string name="settings_use_volume_keys_navigation">تبديل البطاقات باستخدام أزرار الصوت</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">تبديل البطاقات الظاهرة باستخدام أزرار الصوت</string>
|
||||
<string name="settings_category_title_cards_overview">نظرة عامة على البطاقات</string>
|
||||
<string name="settings_column_count_portrait">الأعمدة في الوضع الرأسي</string>
|
||||
<string name="settings_column_count_landscape">الأعمدة في الوضع الأفقي</string>
|
||||
<string name="settings_automatic_column_count">تلقائي</string>
|
||||
<string name="settings_column_count_1">١</string>
|
||||
<string name="settings_column_count_2">٢</string>
|
||||
<string name="settings_column_count_3">٣</string>
|
||||
<string name="settings_column_count_4">٤</string>
|
||||
<string name="settings_column_count_5">٥</string>
|
||||
<string name="settings_column_count_6">٦</string>
|
||||
<string name="settings_column_count_7">٧</string>
|
||||
</resources>
|
||||
@@ -41,7 +41,7 @@
|
||||
<string name="removeImage">Премахване на изображение</string>
|
||||
<string name="takePhoto">Снимане</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="barcodeId">Стойност на щрихкода</string>
|
||||
@@ -51,6 +51,9 @@
|
||||
<string name="importFidme">Внасяне от FidMe</string>
|
||||
<string name="exportOptionExplanation">Данните ще бъдат запазени на място по ваш избор.</string>
|
||||
<string name="accept">Приемане</string>
|
||||
<string name="privacy_policy_popup_text">Политика за личните данни (необходима от някои магазини за приложения):
|
||||
\n
|
||||
\nНЕ СЕ СЪБИРАТ ИЗОБЩО НИКАКВИ ДАННИ, което може да бъде потвърдено, защото приложението е със свободен код.</string>
|
||||
<string name="privacy_policy">Политика за личните данни</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="turn_flashlight_off">Изключва светкавицата</string>
|
||||
@@ -75,7 +78,7 @@
|
||||
<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_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>
|
||||
@@ -154,6 +157,7 @@
|
||||
</plurals>
|
||||
<string name="app_contributors">Осъществено от: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="settings_brown_theme">Кафяво</string>
|
||||
<string name="settings_grey_theme">Сиво</string>
|
||||
<string name="settings_green_theme">Зелено</string>
|
||||
<string name="settings_sky_blue_theme">Небесносиньо</string>
|
||||
<string name="settings_blue_theme">Синьо</string>
|
||||
@@ -203,7 +207,7 @@
|
||||
</plurals>
|
||||
<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>
|
||||
@@ -222,10 +226,14 @@
|
||||
<string name="nextCard">Следваща</string>
|
||||
<string name="failedToOpenUrl">Първо инсталирайте уеб браузър</string>
|
||||
<string name="welcome">Добре дошли при Катима</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="one">Преглед на архива (<xliff:g>%1$d</xliff:g> карта)</item>
|
||||
<item quantity="other">Преглед на архива (<xliff:g>%1$d</xliff:g> карти)</item>
|
||||
</plurals>
|
||||
<string name="barcodeLongPressMessage">В приложението галерия могат да бъдат отваряни само изображения</string>
|
||||
<string name="failedToRetrieveImageFile">Не е възможно извличане на изображение</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">За да сканирате щрихкодове с Catima е необходим достъп до камерата. За да промените разрешението докоснете тук.</string>
|
||||
<string name="updateBalanceTitle">Колко е похарчено или получено?</string>
|
||||
<string name="updateBalanceTitle">Каква е промяната\?</string>
|
||||
<string name="updateBalanceHint">Въведете стойност</string>
|
||||
<string name="newBalanceSentence">Нов баланс: <xliff:g>%s</xliff:g></string>
|
||||
<string name="cameraPermissionDeniedTitle">Камерата е недостъпна</string>
|
||||
@@ -258,7 +266,7 @@
|
||||
<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_cards">Карти</string>
|
||||
<string name="settings_category_title_general">Общи</string>
|
||||
<string name="settings_category_title_privacy">Поверителност</string>
|
||||
<string name="settings_keep_screen_on_summary">Спира автоматичното заключване на екрана при преглед на карти</string>
|
||||
@@ -276,37 +284,4 @@
|
||||
<string name="field_must_not_be_empty">Полето не трябва да е празно</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="settings_follow_sensor_orientation">Винаги да се завърта (пренебрегва системната настройка)</string>
|
||||
<string name="continue_">Продължаване</string>
|
||||
<string name="add_manually_warning_title">Препоръчително е да сканирате</string>
|
||||
<string name="add_manually_warning_message">Стойностите от щрихкода и отбелязаните на картата числа в някои случаи се различават. По тази причина е при ръчно въвеждане картата може да не работи. Силно препоръчително е да сканирате щрихкода с камерата. Желаете ли да продължите въпреки това?</string>
|
||||
<string name="amountParsingFailed">Неприемлива сума</string>
|
||||
<string name="spend">Похарчено</string>
|
||||
<string name="receive">Получено</string>
|
||||
<string name="pageWithNumber">Страница <xliff:g>%d</xliff:g></string>
|
||||
<string name="addFromPdfFile">Изберете PDF</string>
|
||||
<string name="errorReadingFile">Файлът не може да бъде прочетен</string>
|
||||
<string name="failedLaunchingFileManager">Не е намерено приложение за управление на файлове</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Кой от намерените щрихкодове желаете да бъде използван?</string>
|
||||
<string name="noCameraFoundGuideText">Изглежда, устройството няма камера. Ако има опитайте да рестартирате. В противен случай използвайте бутона „Още“, за да добавите шрихкод по друг начин.</string>
|
||||
<string name="importCancelled">Внасянето е спряно</string>
|
||||
<string name="exportCancelled">Изнасянето е спряно</string>
|
||||
<string name="useFrontImage">Използване на предната страна</string>
|
||||
<string name="useBackImage">Използване на задната страна</string>
|
||||
<string name="settings_use_volume_keys_navigation">Сменяне на картите с бутоните за звука</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Бутоните за силата на звука сменят видимата карта</string>
|
||||
<string name="settings_automatic_column_count">Автоматично</string>
|
||||
<string name="settings_column_count_2">2</string>
|
||||
<string name="settings_column_count_4">4</string>
|
||||
<string name="settings_column_count_5">5</string>
|
||||
<string name="settings_column_count_1">1</string>
|
||||
<string name="settings_column_count_3">3</string>
|
||||
<string name="settings_column_count_6">6</string>
|
||||
<string name="settings_column_count_7">7</string>
|
||||
<string name="settings_column_count_landscape">Колони в пейзажен изглед</string>
|
||||
<string name="settings_column_count_portrait">Колони в портретен изглед</string>
|
||||
<string name="settings_category_title_cards_overview">Списък с карти</string>
|
||||
<string name="generic_error_please_retry">Съжаляваме, нещо се обърка, опитайте отново…</string>
|
||||
<string name="addFromPkpass">Изберете файл на Passbook (.pkpass)</string>
|
||||
<string name="unsupportedFile">Този вид файлове не се поддържат</string>
|
||||
<string name="sort_by_valid_from">Начало валидност</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -173,6 +173,9 @@
|
||||
<string name="chooseExpiryDate">মেয়াদ শেষ হওয়ার তারিখ মনোনীত করুন</string>
|
||||
<string name="moveBarcodeToTopOfScreen">বারকোডটি স্ক্রিনের উপরে উঠিয়ে দিন</string>
|
||||
<string name="errorReadingImage">ছবিটি স্ক্যান করা যাচ্ছে না</string>
|
||||
<string name="privacy_policy_popup_text">ব্যক্তিগত তথ্যের গোপনীয়তা নীতি নোটিশ (কিছু অ্যাপ স্টোরের এটি লাগে):
|
||||
\n
|
||||
\nকোন তথ্য একেবারেই সংগ্রহ করা হয় না, যা যে কেউ নিশ্চিত করতে পারবেন কারন আমাদের অ্যাপ মুক্ত সফটওয়্যার।</string>
|
||||
<string name="balance">ব্যালান্স</string>
|
||||
<string name="points">পয়েন্ট</string>
|
||||
<string name="chooseImportType">এখান থেকে তথ্য আমদানি করুন</string>
|
||||
@@ -192,6 +195,10 @@
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> cards (<xliff:g id="archivedCount">%2$d</xliff:g> archived)</item>
|
||||
</plurals>
|
||||
<string name="nextCard">পরবর্তী</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="one">সংরক্ষণাগার দেখুন (<xliff:g>%1$d</xliff:g> কার্ড)</item>
|
||||
<item quantity="other">সংরক্ষণাগার দেখুন (<xliff:g>%1$d</xliff:g> কার্ডগুলি)</item>
|
||||
</plurals>
|
||||
<string name="failedToOpenUrl">প্রথমে একটি ওয়েব ব্রাউজার ইন্সটল করুন</string>
|
||||
<string name="newBalanceSentence">নতুন ব্যালেন্স: <xliff:g>%s</xliff:g></string>
|
||||
<string name="chooseValidFromDate">তারিখ থেকে বৈধ নির্বাচন করুন</string>
|
||||
@@ -212,6 +219,7 @@
|
||||
\nপ্রথমে ভাউচার ভল্টে এক্সপোর্ট টিপে এটি তৈরি করুন।</string>
|
||||
<string name="settings_oled_dark">অন্ধকার থিমের জন্য খাঁটি কালো পটভূমি</string>
|
||||
<string name="setIcon">আইকন সেট করুন</string>
|
||||
<string name="settings_grey_theme">ধূসর</string>
|
||||
<string name="updateBalance">ব্যালেন্স আপডেট করুন</string>
|
||||
<string name="barcodeLongPressMessage">গ্যালারি অ্যাপে শুধুমাত্র ছবি খোলা যাবে</string>
|
||||
<string name="translate_platform">Weblate-এ</string>
|
||||
@@ -235,7 +243,7 @@
|
||||
<string name="unarchive">সংরক্ষণাগারমুক্ত করুন</string>
|
||||
<string name="archived">কার্ড সংরক্ষণাগারভুক্ত</string>
|
||||
<string name="welcome">Catima-তে স্বাগতম</string>
|
||||
<string name="updateBalanceTitle">আপনি কত খরচ করেছেন?</string>
|
||||
<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>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="about">সম্পর্কিত</string>
|
||||
<string name="card">কার্ড</string>
|
||||
<string name="yes">হ্যাঁ</string>
|
||||
<string name="settings_grey_theme">ধূসর</string>
|
||||
<string name="ok">ঠিক আছে</string>
|
||||
<string name="sendLabel">পাঠান…</string>
|
||||
<string name="sort_by_name">নাম</string>
|
||||
@@ -75,4 +76,4 @@
|
||||
<string name="exportFailedTitle">রপ্তানি ব্যর্থ</string>
|
||||
<string name="settings_card_orientation">বারকোড অভিমুখ (ওরিয়েন্টেশন)</string>
|
||||
<string name="app_name">ক্যাটিমা</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<resources>
|
||||
<string name="save">Snimi</string>
|
||||
<string name="cancel">Odustani</string>
|
||||
<string name="unstar">Ukloni iz omiljenih</string>
|
||||
<string name="star">Dodaj u omiljene</string>
|
||||
<string name="barcodeType">Tip barkoda</string>
|
||||
<string name="note">Zabilješka</string>
|
||||
<string name="unstar">Ukloni sve omiljene</string>
|
||||
<string name="star">Omiljene</string>
|
||||
<string name="barcodeType">Barcode tip</string>
|
||||
<string name="note">Bilježnica</string>
|
||||
<string name="storeName">Ime</string>
|
||||
<string name="noMatchingGiftCards">Nema rezultata. Pokušaj promijeniti pretragu.</string>
|
||||
<string name="noGiftCards">Kliknite + Plus dugme da dodate kartu ili uvezete nešto iz menija prvo.</string>
|
||||
<string name="noGiftCards">Kliknite + Plus dugme da dodate kartu ili uvozite nešto iz menija prvo.</string>
|
||||
<string name="action_add">Dodaj</string>
|
||||
<string name="all">Sve</string>
|
||||
<string name="noGroupCards">Ova grupa ne sadrži nikakve karte</string>
|
||||
@@ -104,6 +104,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>
|
||||
<string name="noBarcode">Nema barkoda</string>
|
||||
<string name="deleteTitle">Obriši karticu</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<resources>
|
||||
<string name="groups">Grups</string>
|
||||
<string name="action_add">Afegeix</string>
|
||||
<string name="save">Desa</string>
|
||||
@@ -14,235 +14,4 @@
|
||||
<string name="welcome">Benvingut a Catima</string>
|
||||
<string name="noGiftCards">Cliqueu el botó + més per afegir una targeta, o importeu-ne des del ⋮ menú.</string>
|
||||
<string name="photos">Fotos</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="moveDown">Baixar abaix</string>
|
||||
<string name="setBackImage">Setejar la imatge posterior</string>
|
||||
<string name="setFrontImage">Imatge Frontal</string>
|
||||
<string name="exportPassword">Setegi el password per tal de protegir l\'exportació (opcional)</string>
|
||||
<string name="failedGeneratingShareURL">No s\'ha pogut generar una URL per compartir. Siusplau reporti-ho.</string>
|
||||
<string name="unarchive">Desarxivar</string>
|
||||
<string name="on_google_play">al Google Play</string>
|
||||
<string name="settings_locale">Idioma</string>
|
||||
<string name="field_must_not_be_empty">El camp no pot estar buit</string>
|
||||
<string name="importOptionApplicationTitle">Utilitzar una altra app</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os i contribuïdors</string>
|
||||
<string name="app_copyright_short">Copyright © Sylvia van Os i contribuïdors</string>
|
||||
<string name="app_license">Software lliure Copyleft, licència GPLv3+</string>
|
||||
<string name="app_resources">Recursos lliures de tercers: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="thumbnailDescription">Miniatura</string>
|
||||
<string name="starImage">Estrella de preferides</string>
|
||||
<string name="settings">Configuració</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_light_theme">Tema clar</string>
|
||||
<string name="settings_system_theme">Tema de sistema</string>
|
||||
<string name="settings_dark_theme">Tema Fosc</string>
|
||||
<string name="settings_card_orientation">Orientació de la pantalla</string>
|
||||
<string name="settings_allow_content_provider_read_title">Permet altres apps a accedir a les meves dades</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Desactiva el bloqueix la pantalla mentre es visualitza la targeta</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Les aplicacions han de seguir demanant permís per tenir-hi accés</string>
|
||||
<string name="leaveWithoutSaveTitle">Sortir</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Vols sortir sense grabar?</string>
|
||||
<string name="passwordRequired">Introdueixi el password</string>
|
||||
<string name="turn_flashlight_on">Encendre el llum flash</string>
|
||||
<string name="settings_magenta_theme">Magenta</string>
|
||||
<string name="settings_violet_theme">Violeta</string>
|
||||
<string name="settings_blue_theme">Blau</string>
|
||||
<string name="settings_green_theme">Verd</string>
|
||||
<string name="translate_platform">a la Pàgina Web</string>
|
||||
<string name="report_error">Informar un Error</string>
|
||||
<string name="archived">Targeta arxivada</string>
|
||||
<string name="reverse">...en ordre invers</string>
|
||||
<string name="icon_header_click_text">Pulsa llarg per editar la miniatura</string>
|
||||
<string name="show_note">Mostrar nota</string>
|
||||
<string name="add_manually_warning_title">Recomenem escanejar</string>
|
||||
<string name="add_manually_warning_message">En algunes targetes el valor imprès en la targeta no correspon amb el codi registrat en el codi de barres. Per això, introduint manualment el codi pot no funcionar en alguns casos. Recomanem sempre que sigui possible escanejar la targeta amb la càmera. Vol igualment continuar la edició manual?</string>
|
||||
<string name="continue_">Continuar</string>
|
||||
<string name="exportOptionExplanation">La informació serà escrita al lloc de la seva elecció.</string>
|
||||
<string name="importOptionFilesystemTitle">Importar desde el sistema de fitxers</string>
|
||||
<string name="importOptionFilesystemButton">Desde el sistema de fitxers</string>
|
||||
<string name="selectBarcodeTitle">Sel•lecciona el Codi de Barres</string>
|
||||
<string name="importSuccessful">Dades importades correctament</string>
|
||||
<string name="exportSuccessful">Dades exportades correctament</string>
|
||||
<string name="failedOpeningFileManager">Instala un gestor de fitxers.</string>
|
||||
<string name="showMoreInfo">Mostrar informació</string>
|
||||
<string name="version_history">Històric de versions</string>
|
||||
<string name="sort_by">Ordenar per</string>
|
||||
<string name="sort_by_most_recently_used">Per les més utilitzades</string>
|
||||
<string name="options">Opcions</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> seleccionat</item>
|
||||
<item quantity="many"><xliff:g>%d</xliff:g> seleccionats</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> seleccionats</item>
|
||||
</plurals>
|
||||
<string name="importOptionFilesystemExplanation">Escull un fitxer especific del sistema de fitxers.</string>
|
||||
<string name="no">No</string>
|
||||
<string name="settings_pink_theme">Rosa</string>
|
||||
<string name="sort">Ordenar</string>
|
||||
<string name="failedToRetrieveImageFile">Ha fallat l\'obtenció del fitxer d\'imatge</string>
|
||||
<string name="barcodeLongPressMessage">Les imatges només es poden obrir desde la app galeria</string>
|
||||
<string name="settings_category_title_privacy">Privacitat</string>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Eliminar <xliff:g>%d</xliff:g> targeta</item>
|
||||
<item quantity="many">Eliminar <xliff:g>%d</xliff:g> targetes</item>
|
||||
<item quantity="other">Eliminar <xliff:g>%d</xliff:g> targetes</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Eliminaras aquesta <xliff:g>%d</xliff:g> targeta permanentment?</item>
|
||||
<item quantity="many">Eliminaras aquestes <xliff:g>%d</xliff:g> targetes permanentment?</item>
|
||||
<item quantity="other">Eliminaras aquestes <xliff:g>%d</xliff:g> targetes permanentment?</item>
|
||||
</plurals>
|
||||
<plurals name="groupCardCountWithArchived">
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> targeta (<xliff:g id="archivedCount">%2$d</xliff:g> arxivada)</item>
|
||||
<item quantity="many"><xliff:g>%1$d</xliff:g> targetes (<xliff:g id="archivedCount">%2$d</xliff:g> arxivades)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> targetes (<xliff:g id="archivedCount">%2$d</xliff:g> arxivades)</item>
|
||||
</plurals>
|
||||
<string name="importCancelled">Importació anulada</string>
|
||||
<string name="exportCancelled">Exportació cancelada</string>
|
||||
<string name="noGiftCardsGroup">Crea algunes targetes, asigna-les en un grup aquí.</string>
|
||||
<string name="noMatchingGiftCards">Sense resultats. Prova a canviar la teva cerca.</string>
|
||||
<string name="storeName">Nom</string>
|
||||
<string name="note">Nota</string>
|
||||
<string name="cardId">Id. de la Targeta</string>
|
||||
<string name="barcodeType">Tipus de codi de barres</string>
|
||||
<string name="noBarcode">Sense codi de barres</string>
|
||||
<string name="settings_portrait_orientation">Vertical</string>
|
||||
<string name="yes">Si</string>
|
||||
<string name="addFromPdfFile">Seleccioni un PDF</string>
|
||||
<string name="errorReadingFile">No s\'ha pogut llegir el fitxer</string>
|
||||
<string name="failedLaunchingFileManager">No s\'ha pogut trobar un gestor de fitxers compatible</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Quin dels següents codis de barres prefereix utilitzar?</string>
|
||||
<string name="pageWithNumber">Pàgina <xliff:g>%d</xliff:g></string>
|
||||
<string name="settings_follow_system_orientation">Seguir el sistema</string>
|
||||
<string name="settings_landscape_orientation">Horitzontal</string>
|
||||
<string name="intent_import_card_from_url_share_text">Vull compartir una targeta amb tu</string>
|
||||
<string name="takePhoto">Fer una foto</string>
|
||||
<string name="help_translate_this_app">Ajuda a traduïr aquesta app</string>
|
||||
<string name="license">Llicència</string>
|
||||
<string name="credits">Crèdits</string>
|
||||
<string name="unarchived">Targeta desarxivades</string>
|
||||
<string name="failedLaunchingPhotoPicker">No s\'ha pogut trobar una aplicació de galeria de fotos</string>
|
||||
<string name="previousCard">Anterior</string>
|
||||
<string name="failedToOpenUrl">Instali un navegador abans</string>
|
||||
<string name="importCards">Importar targetes</string>
|
||||
<string name="updateBalanceTitle">Quan ha gastat o ha rebut?</string>
|
||||
<string name="updateBalanceHint">Introdueixi quantitat</string>
|
||||
<string name="anyDate">Qualsevol data</string>
|
||||
<string name="validFromSentence">Vàlid desde: <xliff:g>%s</xliff:g></string>
|
||||
<string name="setBarcodeHeight">Setejar l\'alçada del codi de barres</string>
|
||||
<string name="donate">Donatiu</string>
|
||||
<string name="receive">Rebre</string>
|
||||
<string name="amountParsingFailed">Import invàlid</string>
|
||||
<string name="barcodeImageDescriptionWithType">Codi de barres <xliff:g>%s</xliff:g></string>
|
||||
<string name="about_title_fmt">Sobre <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Versió: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="settings_follow_sensor_orientation">Sempre rota (ignora la configuració de sistema)</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Alguns escàners ho necesiten</string>
|
||||
<string name="settings_keep_screen_on">Mantenir la pantalla encesa</string>
|
||||
<string name="settings_keep_screen_on_summary">Desactiva el bloqueix de la pantalla mentre mostra una targeta</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Evita el bloqueix de la pantalla</string>
|
||||
<string name="enter_group_name">Introdueix el nom del grup</string>
|
||||
<string name="group_edit">Editar el Group</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> targeta</item>
|
||||
<item quantity="many"><xliff:g>%d</xliff:g> targetes</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> targetes</item>
|
||||
</plurals>
|
||||
<string name="group_name_is_empty">El nom del grup ha de contenir algun text</string>
|
||||
<string name="all">Totes</string>
|
||||
<string name="settings_oled_dark">Negre pur en el tema fosc</string>
|
||||
<string name="selectColor">Sel•leccioni el color</string>
|
||||
<string name="setIcon">Setegi la miniatura</string>
|
||||
<string name="settings_theme_color">Color del tema</string>
|
||||
<string name="app_contributors">Fet possible per: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="updateBalance">Actualitzar el balanç</string>
|
||||
<string name="sort_by_name">Nom</string>
|
||||
<string name="archive">Arxivar</string>
|
||||
<string name="currentBalanceSentence">Balanç actual: <xliff:g>%s</xliff:g></string>
|
||||
<string name="height">Alçada:</string>
|
||||
<string name="switchToBackImage">Canviar a la imatge posterior</string>
|
||||
<string name="switchToBarcode">Canviar al codi de barres</string>
|
||||
<string name="show_name_below_image_thumbnail">Mostrar el nom sota de la imatge en miniatura</string>
|
||||
<string name="settings_category_title_cards">Veure la Targeta</string>
|
||||
<string name="enter_card_id">Introdueixi el nombre ID o text de la seva targeta</string>
|
||||
<string name="card_id_must_not_be_empty">ID de la Targeta no pot estar buit</string>
|
||||
<string name="add_a_card_in_a_different_way">Afegeix una targeta de forma diferent</string>
|
||||
<string name="unstar">Treure entre les preferides</string>
|
||||
<string name="star">Afegir a les preferides</string>
|
||||
<string name="cancel">Cancel•la</string>
|
||||
<string name="deleteConfirmation">Vols eliminar de forma permanent aquesta targeta?</string>
|
||||
<string name="share">Compartir</string>
|
||||
<string name="sendLabel">Enviar…</string>
|
||||
<string name="editCardTitle">Editar Targeta</string>
|
||||
<string name="addCardTitle">Afegir Targeta</string>
|
||||
<string name="scanCardBarcode">Escanejar Codi de Barres</string>
|
||||
<string name="cardShortcut">Drecera a la Targeta</string>
|
||||
<string name="noCardsMessage">Afegeix primer una targeta</string>
|
||||
<string name="noCardExistsError">No s\'ha pogut trobar aquesta targeta</string>
|
||||
<string name="failedParsingImportUriError">No s\'ha pogut analitzar la URI d\'importació</string>
|
||||
<string name="openFrontImageInGalleryApp">Obrir la imatge frontal a l\'app de galeria</string>
|
||||
<string name="settings_lock_on_opening_orientation">En obrir la targeta, bloquejar la orientació de la pantalla</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Utilitza els botons de volum per canviar la targeta que es mostra</string>
|
||||
<string name="updateBarcodeQuestionText">Ha canviat el valor ID. Vol actualitzar també el codi de barres per uter utilitzar el mateix valor?</string>
|
||||
<string name="settings_sky_blue_theme">Blau fluix</string>
|
||||
<string name="starred">Preferides</string>
|
||||
<string name="deleteConfirmationGroup">Vols eliminar aquest grup?</string>
|
||||
<string name="removeImage">Eliminar imatge</string>
|
||||
<string name="app_libraries">Llibreries lliures de tercers: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="settings_display_barcode_max_brightness">Màxima iluminació</string>
|
||||
<string name="settings_brown_theme">Marró</string>
|
||||
<string name="manually_enter_barcode_instructions">Introdueixi el ID de la targeta manualment i trii un codi de barres que s\'assembli al de la seva targeta.</string>
|
||||
<string name="rate_this_app">Valora aquesta app</string>
|
||||
<string name="exportPasswordHint">Introdueixi el password</string>
|
||||
<string name="backImageDescription">Imatge posterior</string>
|
||||
<string name="source_repository">Repositori font</string>
|
||||
<string name="on_github">al Github</string>
|
||||
<string name="include_if_asking_support">Si vol demanar suport tècnic, inclogui la següent informació:</string>
|
||||
<string name="newBalanceSentence">Nou balanç: <xliff:g>%s</xliff:g></string>
|
||||
<string name="openBackImageInGalleryApp">Obrir la imatge posterior a l\'app de la galeria</string>
|
||||
<string name="generic_error_please_retry">Perdoni, alguna cosa ha anat malament, siusplau torni-ho a provar...</string>
|
||||
<string name="settings_column_count_6">6</string>
|
||||
<string name="useBackImage">Utilitzar la imatge posterior</string>
|
||||
<string name="sort_by_valid_from">Valid desde</string>
|
||||
<string name="sort_by_expiry">Expiració</string>
|
||||
<string name="and_data_usage">i us de dades</string>
|
||||
<string name="shortcutSelectCard">Sel•leccioni una targeta</string>
|
||||
<string name="duplicateCard">Duplicar</string>
|
||||
<string name="nextCard">Següent</string>
|
||||
<string name="validFromDate">Vàlid desde</string>
|
||||
<string name="chooseValidFromDate">Esculli una data inicial vàlida</string>
|
||||
<string name="switchToFrontImage">Canviar a la imatge frontal</string>
|
||||
<string name="show_balance">Mostrar balanç</string>
|
||||
<string name="show_validity">Mostrar la validesa</string>
|
||||
<string name="settings_category_title_cards_overview">Vista de targetes</string>
|
||||
<string name="settings_column_count_portrait">Columnes en mode vertical</string>
|
||||
<string name="settings_column_count_landscape">Columnes en mode apaisat</string>
|
||||
<string name="settings_automatic_column_count">Automàtic</string>
|
||||
<string name="settings_column_count_1">1</string>
|
||||
<string name="settings_column_count_2">2</string>
|
||||
<string name="settings_column_count_3">3</string>
|
||||
<string name="settings_column_count_4">4</string>
|
||||
<string name="settings_column_count_5">5</string>
|
||||
<string name="settings_column_count_7">7</string>
|
||||
<string name="settings_category_title_general">General</string>
|
||||
<string name="action_display_options">Mostrar opcions</string>
|
||||
<string name="show_archived_cards">Mostrar targetes arxivades</string>
|
||||
<string name="view_online">Vista online</string>
|
||||
<string name="action_more_options">Més opcions</string>
|
||||
<string name="addWithoutBarcode">Afegeix una targeta sebse codi de barres</string>
|
||||
<string name="noCameraFoundGuideText">El seu dispositiu sembla que no té càmera. Si realment en té, provi a reiniciar el dispositiu. Sino utilitzi el botó Més opcions i introdueixi el codi de barres d\'una altra manera.</string>
|
||||
<string name="useFrontImage">Utilitzar la imatge frontal</string>
|
||||
<string name="addFromPkpass">Seleccioni el fitxer Passbook (.pkpass)</string>
|
||||
<string name="unsupportedFile">Aquest fitxer no està soportat</string>
|
||||
<string name="settings_use_volume_keys_navigation">Canviar les targetes al prèmer els botons de volum</string>
|
||||
<string name="noGroups">Clica el botó + per afegir grups per categoritzar.</string>
|
||||
<string name="noGroupCards">Aquest grup està buit</string>
|
||||
<string name="group_name_already_in_use">Ja existeix un grup amb aquest nom</string>
|
||||
<string name="group_updated">Grup actualitzat</string>
|
||||
<string name="moveUp">Pujar amunt</string>
|
||||
<string name="updateBarcodeQuestionTitle">Actualitzar el valor del codi de barres?</string>
|
||||
<string name="turn_flashlight_off">Apagar el llum Flash</string>
|
||||
<string name="settings_oled_dark_summary">Redueix l\'ús de la bateria en pantalles OLED</string>
|
||||
<string name="settings_system_locale">Idioma del sistema</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="spend">Gastar</string>
|
||||
</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">Přidat</string>
|
||||
<string name="noGiftCards">Klepněte na tlačítko plus (+) pro přidání karty nebo naimportujete karty z nabídky (⋮).</string>
|
||||
<string name="noGiftCards">Klepněte na tlačítko Plus (+) pro přidání karty nebo naimportujete karty z nabídky (⋮).</string>
|
||||
<string name="storeName">Název</string>
|
||||
<string name="note">Poznámka</string>
|
||||
<string name="cardId">ID karty</string>
|
||||
@@ -87,6 +87,7 @@
|
||||
<string name="moveDown">Přesunout dolů</string>
|
||||
<string name="moveUp">Přesunout nahoru</string>
|
||||
<string name="settings_brown_theme">Hnědá</string>
|
||||
<string name="settings_grey_theme">Šedá</string>
|
||||
<string name="settings_green_theme">Zelená</string>
|
||||
<string name="settings_sky_blue_theme">Azurová</string>
|
||||
<string name="settings_blue_theme">Modrá</string>
|
||||
@@ -134,6 +135,9 @@
|
||||
\nVytvoříte jej z nabídky Import/Export jiné aplikace Catima tak, že v ní nejprve stisknete tlačítko Exportovat.</string>
|
||||
<string name="importCatima">Import z Catima</string>
|
||||
<string name="accept">Přijmout</string>
|
||||
<string name="privacy_policy_popup_text">Oznámení o zásadách ochrany osobních údajů (vyžadováno některými obchody s aplikacemi):
|
||||
\n
|
||||
\nNejsou shromažďovány žádné údaje, což může potvrdit každý, protože naše aplikace je svobodný software.</string>
|
||||
<string name="privacy_policy">Zásady soukromí</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="chooseImportType">Importovat data z</string>
|
||||
@@ -159,7 +163,7 @@
|
||||
<string name="groups">Skupiny</string>
|
||||
<string name="enter_group_name">Zadejte název skupiny</string>
|
||||
<string name="exportSuccessful">Data exportována</string>
|
||||
<string name="settings_display_barcode_max_brightness">Rozjasnit obrazovku</string>
|
||||
<string name="settings_display_barcode_max_brightness">Vysoký jas při zobrazení čárového kódu</string>
|
||||
<string name="starImage">Hvězdička u oblíbených</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one">Vybrána <xliff:g>%d</xliff:g> karta</item>
|
||||
@@ -213,7 +217,7 @@
|
||||
<string name="archive">Archivovat</string>
|
||||
<string name="unarchive">Vrátit z archivu</string>
|
||||
<string name="unarchived">Karta vrácena z archivu</string>
|
||||
<string name="settings_card_orientation">Orientace obrazovky</string>
|
||||
<string name="settings_card_orientation">Orientace čárového kódu</string>
|
||||
<string name="settings_landscape_orientation">Na šířku</string>
|
||||
<string name="duplicateCard">Duplikovat</string>
|
||||
<string name="archived">Karta archivována</string>
|
||||
@@ -227,6 +231,11 @@
|
||||
<string name="nextCard">Následující</string>
|
||||
<string name="failedToOpenUrl">Nejprve nainstalujte webový prohlížeč</string>
|
||||
<string name="welcome">Vítejte v Catima</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="one">Zobrazit archiv (<xliff:g>%1$d</xliff:g> karta)</item>
|
||||
<item quantity="few">Zobrazit archiv (<xliff:g>%1$d</xliff:g> karty)</item>
|
||||
<item quantity="other">Zobrazit archiv (<xliff:g>%1$d</xliff:g> karet)</item>
|
||||
</plurals>
|
||||
<string name="barcodeLongPressMessage">V aplikaci pro galerii mohou být otevírány pouze obrázky</string>
|
||||
<string name="failedToRetrieveImageFile">Nepodařilo se získat soubor obrázku</string>
|
||||
<string name="cameraPermissionDeniedTitle">Nelze získat přístup k fotoaparátu</string>
|
||||
@@ -234,7 +243,7 @@
|
||||
<string name="updateBalance">Aktualizovat zůstatek</string>
|
||||
<string name="currentBalanceSentence">Současný zůstatek: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Pro skenování čárových kódů bude Catima potřebovat přístup k fotoaparátu. Klepněte zde pro změnu nastavení oprávnění.</string>
|
||||
<string name="updateBalanceTitle">Kolik jste utratil/a nebo obdržel/a?</string>
|
||||
<string name="updateBalanceTitle">Kolik jste utratil\?</string>
|
||||
<string name="updateBalanceHint">Zadejte výši</string>
|
||||
<string name="newBalanceSentence">Nový zůstatek: <xliff:g>%s</xliff:g></string>
|
||||
<string name="storageReadPermissionRequired">Pro tuto akci je potřeba oprávnění ke čtení úložiště…</string>
|
||||
@@ -265,7 +274,7 @@
|
||||
<string name="settings_category_title_privacy">Soukromí</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">U některých čteček je to potřeba</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Při prohlížení karty zabránit zamčení obrazovky</string>
|
||||
<string name="settings_category_title_cards">Zobrazení karty</string>
|
||||
<string name="settings_category_title_cards">Karty</string>
|
||||
<string name="settings_category_title_general">Obecné</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os a přispěvatelé</string>
|
||||
<string name="app_copyright_short">Copyright © Sylvia van Os a přispěvatelé</string>
|
||||
@@ -282,37 +291,4 @@
|
||||
<string name="field_must_not_be_empty">Položka nesmí být prázdná</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="settings_follow_sensor_orientation">Vždy otáčet (ignoruje nastavení systému)</string>
|
||||
<string name="continue_">Pokračovat</string>
|
||||
<string name="add_manually_warning_title">Doporučuje se skenování</string>
|
||||
<string name="add_manually_warning_message">V některých obchodech se hodnota čárového kódu liší od čísla napsaného na kartě. Z tohoto důvodu nemusí ruční zadání čárového kódu vždy fungovat. Důrazně doporučujeme místo toho naskenovat čárový kód pomocí fotoaparátu. Chcete přesto pokračovat?</string>
|
||||
<string name="spend">Utratit</string>
|
||||
<string name="receive">Obdržet</string>
|
||||
<string name="amountParsingFailed">Neplatné množství</string>
|
||||
<string name="addFromPdfFile">Vybrat soubor PDF</string>
|
||||
<string name="errorReadingFile">Soubor nelze přečíst</string>
|
||||
<string name="pageWithNumber">Stránka <xliff:g>%d</xliff:g></string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Který z nalezených čárových kódů chcete použít?</string>
|
||||
<string name="failedLaunchingFileManager">Nelze nalézt podporovaný správce souborů</string>
|
||||
<string name="noCameraFoundGuideText">Zdá se, že vaše zařízení nemá fotoaparát. Pokud ano, zkuste zařízení restartovat. V opačném případě použijte tlačítko Další možnosti a přidejte čárový kód jiným způsobem.</string>
|
||||
<string name="importCancelled">Import zrušen</string>
|
||||
<string name="exportCancelled">Export zrušen</string>
|
||||
<string name="useBackImage">Použijte předchozí obrázek</string>
|
||||
<string name="useFrontImage">Použít přední obrázek</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Pomocí tlačítek hlasitosti můžete změnit, která karta se zobrazí</string>
|
||||
<string name="settings_use_volume_keys_navigation">Přepínat karty pomocí tlačítek hlasitosti</string>
|
||||
<string name="generic_error_please_retry">Je nám líto, něco se pokazilo, zkuste to prosím znovu...</string>
|
||||
<string name="settings_column_count_portrait">Sloupce v režimu na výšku</string>
|
||||
<string name="settings_automatic_column_count">Automatický</string>
|
||||
<string name="addFromPkpass">Vyberte soubor Passbook (.pkpass)</string>
|
||||
<string name="unsupportedFile">Tento soubor není podporován</string>
|
||||
<string name="settings_category_title_cards_overview">Přehled karet</string>
|
||||
<string name="settings_column_count_landscape">Sloupce v režimu na šířku</string>
|
||||
<string name="settings_column_count_1">1</string>
|
||||
<string name="settings_column_count_2">2</string>
|
||||
<string name="settings_column_count_3">3</string>
|
||||
<string name="settings_column_count_6">6</string>
|
||||
<string name="settings_column_count_7">7</string>
|
||||
<string name="settings_column_count_4">4</string>
|
||||
<string name="settings_column_count_5">5</string>
|
||||
<string name="sort_by_valid_from">Platnost od</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" xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="scanCardBarcode">Scan stregkode</string>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="scanCardBarcode">Scanne kortets stregkode</string>
|
||||
<string name="addCardTitle">Tilføj kort</string>
|
||||
<string name="editCardTitle">Rediger kort</string>
|
||||
<string name="sendLabel">Afsend…</string>
|
||||
@@ -24,8 +24,8 @@
|
||||
<string name="cardId">Kort ID</string>
|
||||
<string name="note">Bemærk</string>
|
||||
<string name="storeName">Navn</string>
|
||||
<string name="noMatchingGiftCards">Ingen resultater. Prøv at ændre din søgning.</string>
|
||||
<string name="noGiftCards">Klik på + plus-knappen for at tilføje et kort, eller importer kort fra ⋮ menuen.</string>
|
||||
<string name="noMatchingGiftCards">Jeg fandt ikke noget. Prøv at ændre din søgning.</string>
|
||||
<string name="noGiftCards">Klik på + plus-knappen for at tilføje et kort, eller importer først nogle kort fra ⋮-menuen.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> valgt kort</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> valgte kort</item>
|
||||
@@ -34,13 +34,13 @@
|
||||
<string name="action_search">Søg</string>
|
||||
<string name="importExport">Import/eksport</string>
|
||||
<string name="exportName">Eksport</string>
|
||||
<string name="importExportHelp">Sikkerhedskopiering af dit data, giver dig mulighed for at flytte dem til en anden enhed.</string>
|
||||
<string name="importExportHelp">Hvis du sikkerhedskopierer dine kort, kan du flytte dem til en anden enhed.</string>
|
||||
<string name="importSuccessfulTitle">Importeret</string>
|
||||
<string name="importFailedTitle">Import mislykkedes</string>
|
||||
<string name="importFailed">Kunne ikke udføre importering</string>
|
||||
<string name="importFailed">Kunne ikke importere kort</string>
|
||||
<string name="exportSuccessfulTitle">Eksporteret</string>
|
||||
<string name="exportFailedTitle">Eksport mislykkedes</string>
|
||||
<string name="exportFailed">Kunne ikke udføre eksportering</string>
|
||||
<string name="exportFailed">Kunne ikke eksportere kort</string>
|
||||
<string name="importing">Importere…</string>
|
||||
<string name="exporting">Eksportere…</string>
|
||||
<string name="settings_dark_theme">Mørk</string>
|
||||
@@ -48,7 +48,7 @@
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings">Indstillinger</string>
|
||||
<string name="starImage">Favorit stjerne</string>
|
||||
<string name="thumbnailDescription">Thumbnail</string>
|
||||
<string name="thumbnailDescription">Miniaturebillede til kort</string>
|
||||
<string name="selectBarcodeTitle">Vælg stregkode</string>
|
||||
<string name="app_copyright_old">Baseret på Loyalty Card Keychain
|
||||
\ncopyright © 2016-2020 Branden Archer.</string>
|
||||
@@ -63,95 +63,29 @@
|
||||
<string name="importOptionFilesystemTitle">Import fra filsystem</string>
|
||||
<string name="exportOptionExplanation">Dataene skrives til en placering efter eget valg.</string>
|
||||
<string name="failedParsingImportUriError">Kunne ikke analysere import-URI\'en</string>
|
||||
<string name="noCardExistsError">Kunne ikke finde det kort</string>
|
||||
<string name="noCardExistsError">Kunne ikke finde kort</string>
|
||||
<string name="deleteConfirmationGroup">Slet gruppe\?</string>
|
||||
<string name="all">Alle</string>
|
||||
<string name="noGroupCards">Denne gruppe er tom</string>
|
||||
<string name="noGroups">Klik på + plus-knappen for at tilføje grupper til kategorisering.</string>
|
||||
<string name="noGroupCards">Denne gruppe indeholder ikke nogen kort</string>
|
||||
<string name="noGroups">Klik på + plus-knappen for først at tilføje grupper til kategorisering.</string>
|
||||
<string name="groups">Grupper</string>
|
||||
<string name="enter_group_name">Indtast gruppenavn</string>
|
||||
<string name="exportSuccessful">Data eksporteret</string>
|
||||
<string name="importSuccessful">Data importeret</string>
|
||||
<string name="exportSuccessful">Eksporterede kortdata</string>
|
||||
<string name="importSuccessful">Kortdata importeret</string>
|
||||
<string name="intent_import_card_from_url_share_text">Jeg vil dele et kort med jer</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Forebyg låseskærm</string>
|
||||
<string name="settings_keep_screen_on">LHold skærm tændt</string>
|
||||
<string name="moveUp">Bevæg dig opad</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Forlade uden at gemme\?</string>
|
||||
<string name="settings_display_barcode_max_brightness">Gør skærm lysere</string>
|
||||
<string name="settings_display_barcode_max_brightness">Lysere stregkodevisning</string>
|
||||
<string name="failedOpeningFileManager">Installer først en filhåndteringsprogram.</string>
|
||||
<string name="moveDown">Bevæger sig nedad</string>
|
||||
<string name="leaveWithoutSaveTitle">Afslut</string>
|
||||
<string name="addManually">Indtast stregkoden manuelt</string>
|
||||
<string name="addManually">Indtast kort-ID manuelt</string>
|
||||
<string name="noGiftCardsGroup">Opret kort og tildel dem gupper her.</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Slet dette <xliff:g>%d</xliff:g> kort permanent\?</item>
|
||||
<item quantity="other">Slet disse <xliff:g>%d</xliff:g> kort permanent\?</item>
|
||||
</plurals>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="cameraPermissionRequired">Behov for kamera adgang krævet for denne funktion…</string>
|
||||
<string name="storageReadPermissionRequired">Behov for lager adgang krævet for denne funktion…</string>
|
||||
<string name="permissionReadCardsLabel">Læs Catima Kort</string>
|
||||
<string name="permissionReadCardsDescription">læs dine Catima kort og alle deres detaljer, også noter og billeder</string>
|
||||
<string name="cameraPermissionDeniedTitle">Kunne ikke få adgang til kamera</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">For at scanne stregkoder, har Catima behov for at få adgang til dit kamera. Klik her for at ændre dine tilladelser i indstillinger.</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os og hjælpere</string>
|
||||
<string name="app_copyright_short">Copyright © Sylvia van Os og hjælpere</string>
|
||||
<string name="about_title_fmt">Om <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="app_license">Copylefted libre software, GPLv3+ licens</string>
|
||||
<string name="barcodeImageDescriptionWithType">Billede<xliff:g>%s</xliff:g> stregkode</string>
|
||||
<string name="group_name_already_in_use">Gruppenavn allerede i brug</string>
|
||||
<string name="editGroup">Redigerer Gruppe: <xliff:g>%s</xliff:g></string>
|
||||
<string name="importFidme">Importer fra FidMe</string>
|
||||
<string name="settings_card_orientation">Skærm orientation</string>
|
||||
<string name="settings_follow_system_orientation">Følg system</string>
|
||||
<string name="settings_portrait_orientation">Portræt</string>
|
||||
<string name="settings_landscape_orientation">Landskab</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Deaktiver låseskærm når et kort er åbent</string>
|
||||
<string name="groupsList">Grupper: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentence">Udløber: <xliff:g>%s</xliff:g></string>
|
||||
<string name="moveBarcodeToTopOfScreen">Flyt stregkoden til toppen af skærmen</string>
|
||||
<string name="accept">Acceptér</string>
|
||||
<string name="importCatima">Importer fra Catima</string>
|
||||
<string name="expiryDate">Udløbsdato</string>
|
||||
<string name="never">Aldrig</string>
|
||||
<string name="chooseExpiryDate">Vælg udløbsdato</string>
|
||||
<string name="balance">Balance</string>
|
||||
<string name="importStocard">Importer fra Stocard</string>
|
||||
<string name="balanceSentence">Balance: <xliff:g>%s</xliff:g></string>
|
||||
<string name="group_name_is_empty">Gruppenavn må ikke være tom</string>
|
||||
<string name="group_updated">Gruppe opdateret</string>
|
||||
<string name="card">Kort</string>
|
||||
<string name="currency">Valuta</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Nødvendigt for nogle scannere til at fungerer</string>
|
||||
<string name="editBarcode">Rediger stregkode</string>
|
||||
<string name="points">Point</string>
|
||||
<string name="privacy_policy">Privatlivspolitik</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> kort</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> kort</item>
|
||||
</plurals>
|
||||
<string name="settings_allow_content_provider_read_title">Tillid andre apps tilgang til min data</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Apps vil stadig have behov for at spørge om tilladelse for at få adgang</string>
|
||||
<string name="barcodeId">Stregkode værdi</string>
|
||||
<string name="setBarcodeId">Vælg stregkode værdi</string>
|
||||
<string name="sameAsCardId">Samme som ID</string>
|
||||
<string name="settings_system_theme">System</string>
|
||||
<string name="settings_lock_on_opening_orientation">Lås til orientation når kort åbnes</string>
|
||||
<string name="settings_keep_screen_on_summary">Deaktiver skærm tids slukning når et kort er åbent</string>
|
||||
<string name="group_edit">Rediger gruppe</string>
|
||||
<string name="settings_follow_sensor_orientation">Altid roter (ignorer system indstillinger)</string>
|
||||
<string name="chooseImportType">Importer data fra</string>
|
||||
<string name="importVoucherVault">Importer fra Voucher Vault</string>
|
||||
<string name="settings_use_volume_keys_navigation">Skift kort ved brug af lydstyrke knapperne</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Brug lydstyrke knapperne til at ændre hvilket kort bliver vist</string>
|
||||
<string name="addFromImage">Vælg et billede fra galleri</string>
|
||||
<string name="expiryStateSentenceExpired">Udløbet: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noBarcodeFound">Ingen stregkode blev fundet</string>
|
||||
<string name="errorReadingImage">Kunne ikke læse billedet</string>
|
||||
<string name="balanceParsingFailed">Ugyldig balance</string>
|
||||
<plurals name="balancePoints">
|
||||
<item quantity="one"><xliff:g>%s</xliff:g> point</item>
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> point</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -12,16 +12,16 @@
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="delete">Löschen</string>
|
||||
<string name="confirm">Bestätigen</string>
|
||||
<string name="star">Zu Favoriten hinzufügen</string>
|
||||
<string name="unstar">Aus Favoriten entfernen</string>
|
||||
<string name="star">Zu den Favoriten hinzufügen</string>
|
||||
<string name="unstar">Aus den Favoriten entfernen</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="sendLabel">Senden…</string>
|
||||
<string name="sendLabel">Senden …</string>
|
||||
<string name="editCardTitle">Karte bearbeiten</string>
|
||||
<string name="addCardTitle">Karte hinzufügen</string>
|
||||
<string name="addCardTitle">Neue Karte</string>
|
||||
<string name="scanCardBarcode">Barcode scannen</string>
|
||||
<string name="cardShortcut">Kartenkürzel</string>
|
||||
<string name="cardShortcut">Shortcut zu einer Karte</string>
|
||||
<string name="noCardsMessage">Füge zuerst eine Karte hinzu</string>
|
||||
<string name="noCardExistsError">Konnte die Karte nicht finden</string>
|
||||
<string name="noCardExistsError">Diese Karte konnte nicht gefunden werden</string>
|
||||
<string name="importExport">Import/Export</string>
|
||||
<string name="exportName">Export</string>
|
||||
<string name="importExportHelp">Wenn du deine Daten sicherst, kannst du sie auf ein anderes Gerät übertragen.</string>
|
||||
@@ -33,12 +33,12 @@
|
||||
<string name="exportFailed">Export konnte nicht durchgeführt werden</string>
|
||||
<string name="importing">Importiere…</string>
|
||||
<string name="exporting">Exportiere…</string>
|
||||
<string name="importOptionFilesystemTitle">Aus Dateisystem importieren</string>
|
||||
<string name="importOptionFilesystemExplanation">Wähle eine bestimmte Datei aus dem Dateisystem aus.</string>
|
||||
<string name="importOptionFilesystemButton">vom Dateisystem</string>
|
||||
<string name="importOptionApplicationTitle">Andere App verwenden</string>
|
||||
<string name="importOptionApplicationExplanation">Benutze eine beliebige App oder deinen bevorzugten Dateimanager zur Dateiauswahl.</string>
|
||||
<string name="importOptionApplicationButton">Andere App verwenden</string>
|
||||
<string name="importOptionFilesystemTitle">Importiere aus dem Dateisystem</string>
|
||||
<string name="importOptionFilesystemExplanation">Wähle eine Datei vom Dateisystem aus.</string>
|
||||
<string name="importOptionFilesystemButton">Wähle vom Dateisystem</string>
|
||||
<string name="importOptionApplicationTitle">Andere Anwendungen</string>
|
||||
<string name="importOptionApplicationExplanation">Beliebige Anwendung oder deinen bevorzugten Dateimanager zur Dateiauswahl verwenden.</string>
|
||||
<string name="importOptionApplicationButton">Aus anderer Anwendung</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="app_license">Freie Software, lizensiert unter der GPLv3+</string>
|
||||
<string name="about_title_fmt">Über <xliff:g id="app_name">%s</xliff:g></string>
|
||||
@@ -46,7 +46,7 @@
|
||||
<string name="selectBarcodeTitle">Barcode auswählen</string>
|
||||
<string name="thumbnailDescription">Vorschaubild</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="settings_display_barcode_max_brightness">Displayhelligkeit erhöhen</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>
|
||||
<string name="intent_import_card_from_url_share_text">Ich würde gerne diese Karte mit dir teilen</string>
|
||||
@@ -66,12 +66,12 @@
|
||||
<string name="noGroups">Klicke auf das Pluszeichen +, um eine Gruppe hinzuzufügen.</string>
|
||||
<string name="noGroupCards">Diese Gruppe ist leer</string>
|
||||
<string name="groups">Gruppen</string>
|
||||
<string name="enter_group_name">Gruppennamen eingeben</string>
|
||||
<string name="enter_group_name">Gib einen Gruppennamen ein</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Beenden ohne zu speichern\?</string>
|
||||
<string name="leaveWithoutSaveTitle">Beenden</string>
|
||||
<string name="failedOpeningFileManager">Installiere zuerst einen Dateimanager.</string>
|
||||
<string name="noBarcode">Kein Barcode</string>
|
||||
<string name="addManually">Barcode manuell eingeben</string>
|
||||
<string name="addManually">Code manuell eingeben</string>
|
||||
<string name="moveDown">Nach unten verschieben</string>
|
||||
<string name="moveUp">Nach oben verschieben</string>
|
||||
<plurals name="groupCardCount">
|
||||
@@ -95,23 +95,28 @@
|
||||
<string name="expiryStateSentence">Läuft ab: <xliff:g>%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Sperrbildschirm verhindern</string>
|
||||
<string name="settings_keep_screen_on">Bildschirm aktiv lassen</string>
|
||||
<string name="privacy_policy_popup_text">Hinweis zum Datenschutz (oft gefordert):
|
||||
\n
|
||||
\nKEINE DATEN WERDEN GESAMMELT, was jeder bestätigen kann, da unsere Anwendung eine freie Software ist.</string>
|
||||
<string name="accept">Annehmen</string>
|
||||
<string name="privacy_policy">Datenschutzrichtlinie</string>
|
||||
<string name="importVoucherVaultMessage">Wähle deinen <i>vouchervault.json</i>-Export aus Voucher Vault zum Importieren aus. \nErstelle ihn, indem du zuerst auf Export in Voucher Vault drückst.</string>
|
||||
<string name="importVoucherVaultMessage">Wähle deinen <i>vouchervault.json</i>-Export aus Voucher Vault zum Importieren aus.
|
||||
\nErstellen Sie ihn, indem du zuerst auf Export in Voucher Vault drückst.</string>
|
||||
<string name="importVoucherVault">Aus Voucher Vault importieren</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Wählen du deinen <i>LoyaltyCardKeychain.csv</i>-Export aus Loyalty Card Keychain zum Importieren aus.
|
||||
\nErstelle ihn über das Menü Import/Export in Loyalty Card Keychain, indem du dort zuerst auf Export drückst.</string>
|
||||
<string name="importLoyaltyCardKeychain">Aus Loyalty Card Keychain importieren</string>
|
||||
<string name="importFidmeMessage">Wähle deinen <i>fidme-export-request-xxxxxx.zip</i>-Export aus FidMe zum Importieren aus und wähle anschließend die Barcodetypen manuell aus. \nOder erstelle ihn aus deinem FidMe-Profil, indem du Datenschutz wählst und dann zuerst auf Meine Daten extrahieren drückst.</string>
|
||||
<string name="importFidmeMessage">Wähle deinen <i>fidme-export-request-xxxxxx.zip</i>-Export aus FidMe zum Importieren aus und wähle anschließend die Strichcodetypen manuell aus.
|
||||
\nOder erstelle ihn aus deinem FidMe-Profil, indem du Datenschutz wählst und dann zuerst auf Meine Daten extrahieren drückst.</string>
|
||||
<string name="importFidme">Aus FidMe importieren</string>
|
||||
<string name="importCatimaMessage">Wähle deinen <i>catima.zip</i>-Export aus Catima zum Importieren aus.
|
||||
\nErstelle ihn aus dem Import/Export-Menü einer anderen Catima-Anwendung, indem du dort zuerst Export drückst.</string>
|
||||
<string name="importCatima">Aus Catima importieren</string>
|
||||
<string name="setBarcodeId">Barcodewert festlegen</string>
|
||||
<string name="setBarcodeId">Manuell eingeben</string>
|
||||
<string name="sameAsCardId">Entspricht Kartennummer</string>
|
||||
<string name="barcodeId">Barcodewert</string>
|
||||
<string name="errorReadingImage">Konnte das Bild nicht lesen</string>
|
||||
<string name="noBarcodeFound">Keinen Barcode erkannt</string>
|
||||
<string name="errorReadingImage">Bildverarbeitung fehlgeschlagen</string>
|
||||
<string name="noBarcodeFound">Kein Barcode erkannt</string>
|
||||
<string name="addFromImage">Bild aus der Galerie wählen</string>
|
||||
<string name="unsupportedBarcodeType">Dieser Barcodetyp kann noch nicht angezeigt werden. Wir hoffen das Format in einer zukünftigen Version zu unterstützen.</string>
|
||||
<string name="wrongValueForBarcodeType">Der Wert ist für den gewählten Barcodetyp leider nicht gültig</string>
|
||||
@@ -124,23 +129,24 @@
|
||||
<string name="updateBarcodeQuestionTitle">Barcodewert aktualisieren?</string>
|
||||
<string name="takePhoto">Foto aufnehmen</string>
|
||||
<string name="removeImage">Bild entfernen</string>
|
||||
<string name="setBackImage">Rückseite einstellen</string>
|
||||
<string name="setFrontImage">Vorderseite einstellen</string>
|
||||
<string name="setBackImage">Kartenrückseite</string>
|
||||
<string name="setFrontImage">Kartenvorderseite</string>
|
||||
<string name="photos">Fotos</string>
|
||||
<string name="frontImageDescription">Vorderseite</string>
|
||||
<string name="backImageDescription">Rückseite</string>
|
||||
<string name="frontImageDescription">Bild der Vorseite</string>
|
||||
<string name="backImageDescription">Bild der Rückseite</string>
|
||||
<string name="passwordRequired">Bitte gib das Passwort ein</string>
|
||||
<string name="importStocardMessage">Wähle deinen <i>***.zip</i>-Export aus Stocard zum Importieren aus. \nDu erhälst ihn, indem du eine E-Mail an support@stocardapp.com sendest und um einen Export deiner Daten bittest.</string>
|
||||
<string name="importStocardMessage">Wähle deinen <i>***.zip</i>-Export aus Stocard zum Importieren aus.
|
||||
\nSie erhalten ihn, indem du eine E-Mail an support@stocardapp.com sendest und um einen Export deiner Daten bitten.</string>
|
||||
<string name="importStocard">Von Stocard importieren</string>
|
||||
<string name="turn_flashlight_off">Blitzlicht ausschalten</string>
|
||||
<string name="turn_flashlight_on">Blitzlicht einschalten</string>
|
||||
<string name="turn_flashlight_off">Licht ausschalten</string>
|
||||
<string name="turn_flashlight_on">Licht einschalten</string>
|
||||
<string name="failedGeneratingShareURL">URL konnte nicht erstellt werden. Bitte melde das an uns.</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> ausgewählt</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> ausgewählt</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Karte löschen</string>
|
||||
<string name="deleteConfirmation">Diese Karte endgültig löschen?</string>
|
||||
<string name="deleteConfirmation">Diese Karte wirklich löschen?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Diese <xliff:g>%d</xliff:g> Karte endgültig löschen\?</item>
|
||||
<item quantity="other">Diese <xliff:g>%d</xliff:g> Karten endgültig löschen\?</item>
|
||||
@@ -152,6 +158,7 @@
|
||||
<string name="settings_system_locale">System</string>
|
||||
<string name="settings_locale">Sprache</string>
|
||||
<string name="settings_brown_theme">Braun</string>
|
||||
<string name="settings_grey_theme">Grau</string>
|
||||
<string name="settings_green_theme">Grün</string>
|
||||
<string name="settings_sky_blue_theme">Himmelblau</string>
|
||||
<string name="settings_blue_theme">Blau</string>
|
||||
@@ -163,16 +170,16 @@
|
||||
<string name="app_contributors">Ermöglicht durch: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="barcodeImageDescriptionWithType">Bild <xliff:g>%s</xliff:g> Barcode</string>
|
||||
<string name="sort_by">Sortieren nach</string>
|
||||
<string name="sort_by_expiry">Gültig bis</string>
|
||||
<string name="sort_by_expiry">Ablauf</string>
|
||||
<string name="sort_by_most_recently_used">Zuletzt verwendet</string>
|
||||
<string name="sort_by_name">Name</string>
|
||||
<string name="sort">Sortieren</string>
|
||||
<string name="reverse">…in umgekehrter Reihenfolge</string>
|
||||
<string name="version_history">Versionshistorie</string>
|
||||
<string name="credits">Dank an</string>
|
||||
<string name="help_translate_this_app">Hilf bei der Übersetzung</string>
|
||||
<string name="help_translate_this_app">Hilfe bei der Übersetzung</string>
|
||||
<string name="and_data_usage">und Datennutzung</string>
|
||||
<string name="rate_this_app">Bewerte die App</string>
|
||||
<string name="rate_this_app">Bewerte die Anwendung</string>
|
||||
<string name="on_google_play">auf Google Play</string>
|
||||
<string name="license">Lizenz</string>
|
||||
<string name="source_repository">Quellcode</string>
|
||||
@@ -186,10 +193,10 @@
|
||||
<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">Vorschaubild festlegen</string>
|
||||
<string name="setIcon">Vorschaubild setzen</string>
|
||||
<string name="selectColor">Farbe auswählen</string>
|
||||
<string name="translate_platform">auf Weblate</string>
|
||||
<string name="shortcutSelectCard">Karte wählen</string>
|
||||
<string name="shortcutSelectCard">Wählen Sie eine Karte</string>
|
||||
<string name="options">Optionen</string>
|
||||
<string name="showMoreInfo">Infos anzeigen</string>
|
||||
<string name="starred">als Favorit</string>
|
||||
@@ -203,9 +210,9 @@
|
||||
<string name="settings_landscape_orientation">Querformat</string>
|
||||
<string name="settings_portrait_orientation">Hochformat</string>
|
||||
<string name="duplicateCard">Duplizieren</string>
|
||||
<string name="unarchive">Aus dem Archiv wiederherstellen</string>
|
||||
<string name="settings_card_orientation">Bildschirm-Ausrichtung</string>
|
||||
<string name="unarchived">Karte aus dem Archiv wiederhergestellt</string>
|
||||
<string name="unarchive">Archivierung aufheben</string>
|
||||
<string name="settings_card_orientation">Barcode-Ausrichtung</string>
|
||||
<string name="unarchived">Karte aus dem Archiv entfernt</string>
|
||||
<string name="archive">Archivieren</string>
|
||||
<string name="archived">Karte archiviert</string>
|
||||
<string name="settings_lock_on_opening_orientation">Kartenausrichtung nach dem Öffnen beibehalten</string>
|
||||
@@ -213,16 +220,20 @@
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> Karte (<xliff:g id="archivedCount">%2$d</xliff:g> archiviert)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> Karten (<xliff:g id="archivedCount">%2$d</xliff:g> archiviert)</item>
|
||||
</plurals>
|
||||
<string name="failedLaunchingPhotoPicker">Es konnte keine unterstützte Galerie-App gefunden werden</string>
|
||||
<string name="failedLaunchingPhotoPicker">Es konnte keine unterstützte Galerie-Anwendung gefunden werden</string>
|
||||
<string name="previousCard">Vorherige</string>
|
||||
<string name="nextCard">Nächste</string>
|
||||
<string name="failedToOpenUrl">Bitte installiere zuerst einen Webbrowser</string>
|
||||
<string name="failedToOpenUrl">Bitte installiere einen Webbrowser</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="one">Archiv ansehen (<xliff:g>%1$d</xliff:g> Karte)</item>
|
||||
<item quantity="other">Archiv ansehen (<xliff:g>%1$d</xliff:g> Karten)</item>
|
||||
</plurals>
|
||||
<string name="welcome">Willkommen bei Catima</string>
|
||||
<string name="barcodeLongPressMessage">In der Galerie können nur Bilder geöffnet werden</string>
|
||||
<string name="failedToRetrieveImageFile">Bilddatei konnte nicht abgerufen werden</string>
|
||||
<string name="updateBalanceTitle">Wie viel hast du ausgegeben oder erhalten?</string>
|
||||
<string name="updateBalanceTitle">Wie viel haben Sie ausgegeben\?</string>
|
||||
<string name="cameraPermissionDeniedTitle">Kein Zugriff auf die Kamera möglich</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Um Barcodes zu scannen, benötigt Catima Zugriff auf deine Kamera. Tippe hier, um deine Berechtigungseinstellungen zu ändern.</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Um Strichcodes zu scannen, benötigt Catima Zugriff auf Ihre Kamera. Tippen Sie hier, um Ihre Berechtigungseinstellungen zu ändern.</string>
|
||||
<string name="updateBalanceHint">Betrag eingeben</string>
|
||||
<string name="importCards">Karten importieren</string>
|
||||
<string name="currentBalanceSentence">Aktuelles Guthaben: <xliff:g>%s</xliff:g></string>
|
||||
@@ -234,76 +245,43 @@
|
||||
<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">Zum Bearbeiten des Vorschaubildes lang drücken</string>
|
||||
<string name="switchToBarcode">Zum Barcode wechseln</string>
|
||||
<string name="openFrontImageInGalleryApp">Vorderseite in Galerie öffnen</string>
|
||||
<string name="openBackImageInGalleryApp">Rückseite in Galerie öffnen</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">Zur Vorderseite wechseln</string>
|
||||
<string name="switchToBackImage">Zur Rückseite wechseln</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 Vorschaubild anzeigen</string>
|
||||
<string name="settings_allow_content_provider_read_title">Anderen Apps den Zugriff auf meine Daten gestatten</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 deiner Catima Karten mit allen Details, einschließlich Notizen und Bildern</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Apps müssen weiterhin eine Genehmigung beantragen, um Zugriff zu erhalten</string>
|
||||
<string name="permissionReadCardsDescription">lesen deiner Catima 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">Kartenansicht</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>
|
||||
<string name="view_online">Online anzeigen</string>
|
||||
<string name="view_online">Zeige online</string>
|
||||
<string name="action_display_options">Anzeigeoptionen</string>
|
||||
<string name="show_archived_cards">Archivierte Karten anzeigen</string>
|
||||
<string name="show_archived_cards">Zeige archivierte Karten</string>
|
||||
<string name="app_copyright_short">Copyright © Sylvia van Os und Mitwirkende</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019–<xliff:g>%d</xliff:g> Sylvia van Os und Mitwirkende</string>
|
||||
<string name="card_id_must_not_be_empty">Kartennummer darf nicht leer sein</string>
|
||||
<string name="balanceParsingFailed">Ungültiges Guthaben</string>
|
||||
<string name="add_a_card_in_a_different_way">Karte anders hinzufügen</string>
|
||||
<string name="action_more_options">Mehr Optionen</string>
|
||||
<string name="enter_card_id">Gib die Kartennummer oder den Text auf deiner Karte ein</string>
|
||||
<string name="addWithoutBarcode">Eine Karte ohne Barcode hinzufügen</string>
|
||||
<string name="enter_card_id">Gib die Codenummer oder Ziffernfolge deiner Karte an</string>
|
||||
<string name="addWithoutBarcode">Eine Karde ohne Code hinzufügen</string>
|
||||
<string name="field_must_not_be_empty">Feld darf nicht leer sein</string>
|
||||
<string name="manually_enter_barcode_instructions">Trage die Kartenummer oder Text deiner Karte ein und drücke auf den Barcode, der wie der auf deiner Karte aussieht.</string>
|
||||
<string name="manually_enter_barcode_instructions">Trage die ID Nummer oder Text deiner Karte ein und drücke auf den Barcode, der wie auf deiner Karte aussieht.</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="settings_follow_sensor_orientation">Immer drehen (ignoriert Systemeinstellungen)</string>
|
||||
<string name="continue_">Fortfahren</string>
|
||||
<string name="add_manually_warning_title">Scannen empfohlen</string>
|
||||
<string name="add_manually_warning_message">In einigen Geschäften weicht der Wert des Barcodes von dem auf der Karte angegebenen Wert ab. Aus diesem Grund funktioniert die manuelle Eingabe des Barcodes in einigen Fällen nicht. Es wird dringend empfohlen, den Barcode mit einer Kamera zu scannen. Möchtest du dennoch fortfahren?</string>
|
||||
<string name="spend">Zahlen</string>
|
||||
<string name="receive">Erhalten</string>
|
||||
<string name="amountParsingFailed">Ungültiger Betrag</string>
|
||||
<string name="addFromPdfFile">PDF-Datei auswählen</string>
|
||||
<string name="errorReadingFile">Datei konnte nicht gelesen werden</string>
|
||||
<string name="failedLaunchingFileManager">Konnte keinen unterstützten Dateimanager finden</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Welchen der gefundenen Barcodes möchtest du verwenden?</string>
|
||||
<string name="pageWithNumber">Seite <xliff:g>%d</xliff:g></string>
|
||||
<string name="noCameraFoundGuideText">Dein Gerät scheint nicht über eine Kamera zu verfügen. Falls doch, versuche, das Gerät neu zu starten. Andernfalls verwende die Schaltfläche Weitere Optionen unten, um einen Barcode auf andere Weise hinzuzufügen.</string>
|
||||
<string name="exportCancelled">Export abgebrochen</string>
|
||||
<string name="importCancelled">Import abgebrochen</string>
|
||||
<string name="useFrontImage">Vorderseite verwenden</string>
|
||||
<string name="useBackImage">Rückseite verwenden</string>
|
||||
<string name="settings_use_volume_keys_navigation">Karten mit Lautstärketasten wechseln</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Wähle mit den Lautstärketasten, welche Karte angezeigt werden soll</string>
|
||||
<string name="settings_column_count_portrait">Spalten im Hochformat</string>
|
||||
<string name="settings_column_count_landscape">Spalten im Querformat</string>
|
||||
<string name="settings_automatic_column_count">Automatisch</string>
|
||||
<string name="settings_column_count_2">2</string>
|
||||
<string name="settings_category_title_cards_overview">Kartenübersicht</string>
|
||||
<string name="settings_column_count_1">1</string>
|
||||
<string name="settings_column_count_7">7</string>
|
||||
<string name="settings_column_count_3">3</string>
|
||||
<string name="settings_column_count_4">4</string>
|
||||
<string name="settings_column_count_5">5</string>
|
||||
<string name="settings_column_count_6">6</string>
|
||||
<string name="generic_error_please_retry">Entschuldigung, da ist etwas schief gelaufen, versuchen Sie es noch einmal ...</string>
|
||||
<string name="unsupportedFile">Diese Datei wird nicht unterstützt</string>
|
||||
<string name="addFromPkpass">Passbook-Datei (.pkpass) auswählen</string>
|
||||
<string name="sort_by_valid_from">Gültig ab</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -4,7 +4,7 @@
|
||||
<string name="noGiftCards">Κάντε κλικ στο + κουμπί για να προσθέσετε μία κάρτα ή προσθέστε από το ⋮ μενού.</string>
|
||||
<string name="storeName">Όνομα</string>
|
||||
<string name="note">Σημείωση</string>
|
||||
<string name="cardId">Κωδικός κάρτας</string>
|
||||
<string name="cardId">Κωδικός Κάρτας</string>
|
||||
<string name="cancel">Άκυρο</string>
|
||||
<string name="save">Αποθήκευση</string>
|
||||
<string name="edit">Επεξεργασία</string>
|
||||
@@ -14,13 +14,13 @@
|
||||
<string name="sendLabel">Αποστολή…</string>
|
||||
<string name="editCardTitle">Επεξεργασία Κάρτας</string>
|
||||
<string name="addCardTitle">Προσθήκη Κάρτας</string>
|
||||
<string name="scanCardBarcode">Σαρώστε τον γραμμωτό κώδικα</string>
|
||||
<string name="scanCardBarcode">Σαρώστε τον γραμμοκώδικα (bardcode)</string>
|
||||
<string name="cardShortcut">Συντόμευση Κάρτας</string>
|
||||
<string name="noCardsMessage">Προσθέστε μία κάρτα πρώτα</string>
|
||||
<string name="noCardExistsError">Δεν ήταν δυνατό να εντοπιστεί η κάρτα</string>
|
||||
<string name="importExport">Εισαγωγή/Εξαγωγή</string>
|
||||
<string name="exportName">Εξαγωγή</string>
|
||||
<string name="importExportHelp">Τα αντίγραφα ασφαλείας σας επιτρέπουν να τα εισάγετε σε άλλη συσκευή.</string>
|
||||
<string name="importExportHelp">Τα αντίγραφα ασφαλείας, σας επιτρέπουν να τα εισάγετε σε άλλη συσκευή.</string>
|
||||
<string name="importSuccessfulTitle">Εισήχθησαν</string>
|
||||
<string name="importFailedTitle">Εισαγωγή ανεπιτυχής</string>
|
||||
<string name="importFailed">Δεν ήταν δυνατή η εισαγωγή</string>
|
||||
@@ -39,7 +39,7 @@
|
||||
<string name="app_license">Άδεια χρήσης υπό GPLv3+</string>
|
||||
<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="selectBarcodeTitle">Επιλέξτε γραμμωτό κώδικα</string>
|
||||
<string name="selectBarcodeTitle">Επιλέξτε Barcode</string>
|
||||
<string name="thumbnailDescription">Μικρογραφία</string>
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="settings_dark_theme">Σκοτεινό</string>
|
||||
@@ -54,7 +54,7 @@
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> επιλέγχθηκαν</item>
|
||||
</plurals>
|
||||
<string name="noGiftCardsGroup">Δημιούργησε κάρτες και βάλτες σε αυτή την ομάδα.</string>
|
||||
<string name="addManually">Εισαγωγή γραμμωτού κώδικα με μη αυτόματο τρόπο</string>
|
||||
<string name="addManually">Χειροκίνητη εισαγωγή κωδικού</string>
|
||||
<string name="never">Ποτέ</string>
|
||||
<string name="share">Κοινοποίηση</string>
|
||||
<plurals name="balancePoints">
|
||||
@@ -64,19 +64,19 @@
|
||||
<string name="exportOptionExplanation">Τα δεδομένα θα μεταφερθούν σε τοποθεσία της επιλογής σας.</string>
|
||||
<string name="settings_theme">Θέμα</string>
|
||||
<string name="groupsList">Ομάδες: <xliff:g>%s</xliff:g></string>
|
||||
<string name="barcodeId">Τιμή γραμμωτού κώδικα</string>
|
||||
<string name="barcodeId">Τιμή γραμμοκώδικα</string>
|
||||
<string name="sort">Ταξινόμηση</string>
|
||||
<string name="deleteConfirmationGroup">Διαγραφή ομάδας;</string>
|
||||
<string name="moveDown">Προχώρα κάτω</string>
|
||||
<string name="accept">Αποδοχή</string>
|
||||
<string name="yes">Ναι</string>
|
||||
<string name="no">Όχι</string>
|
||||
<string name="addFromImage">Επιλογή εικόνας από τη συλλογή</string>
|
||||
<string name="addFromImage">Επιλογή εικόνας από την συλλογή</string>
|
||||
<string name="expiryStateSentence">Λήγει: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryDate">Ημερομηνία λήξης</string>
|
||||
<string name="settings_keep_screen_on">Διατήρηση ενεργής οθόνης</string>
|
||||
<string name="settings_keep_screen_on">Κράτα την οθόνη ανοιχτή</string>
|
||||
<string name="leaveWithoutSaveTitle">Έξοδος</string>
|
||||
<string name="reverse">...σε αντίστροφη σειρά</string>
|
||||
<string name="reverse">... σε αντίθετη σειρά</string>
|
||||
<string name="version_history">Ιστορικό έκδοσης</string>
|
||||
<string name="sort_by">Ταξινόμηση κατά</string>
|
||||
<string name="credits">Πιστώσεις</string>
|
||||
@@ -91,17 +91,18 @@
|
||||
<string name="importLoyaltyCardKeychainMessage">Επιλέξτε την <i>LoyaltyCardKeychain.csv</i> εξαγωγή από το Loyalty Card Keychain για εισαγωγή.
|
||||
\nΔημιουργήστε το από το μενού Εισαγωγής/Εξαγωγής στο Loyalty Card Keychain επιλέγοντας Εξαγωγή.</string>
|
||||
<string name="importFidme">Εισαγωγή από FidMe</string>
|
||||
<string name="importFidmeMessage">Επιλέξτε την <i>fidme-export-request-xxxxxx.zip</i> εξαγωγή από το FidMe για εισαγωγή και επιλέξτε χειροκίνητα τους τύπους γραμμωτού κώδικα μετέπειτα.\nΔημιουργήστε το από το FidMe προφίλ επιλέγοντας Προστασία Δεδομένων και πατώντας Εξαγωγή δεδομένων πρώτα.</string>
|
||||
<string name="setBarcodeId">Επιλέξτε τιμή γραμμωτού κώδικα</string>
|
||||
<string name="wrongValueForBarcodeType">Η τιμή δεν είναι έγκυρη για τον επιλεγμένο γραμμωτό κώδικα</string>
|
||||
<string name="importFidmeMessage">Επιλέξτε την <i>fidme-export-request-xxxxxx.zip</i> εξαγωγή από το FidMe για εισαγωγή και επιλέξτε χειροκίνητα τους τύπους γραμμοκώδικα μετέπειτα.
|
||||
\nΔημιουργήστε το από το FidMe προφίλ επιλέγοντας Προστασία Δεδομένων και διαλέγοντας εξαγωγή δεδομένων.</string>
|
||||
<string name="setBarcodeId">Επιλέξτε τιμή γραμμοκώδικα</string>
|
||||
<string name="wrongValueForBarcodeType">Η τιμή δεν είναι έγκυρη για αυτού του τύπου γραμμοκώδικα</string>
|
||||
<string name="setBackImage">Επιλογή οπίσθιας εικόνας</string>
|
||||
<string name="removeImage">Αφαίρεση εικόνας</string>
|
||||
<string name="takePhoto">Τραβήξτε μια φωτογραφία</string>
|
||||
<string name="updateBarcodeQuestionText">Αλλάξατε τον κωδικό. Θέλετε να ενημερώσετε και τον γραμμωτό κώδικα στην ίδια τιμή;</string>
|
||||
<string name="updateBarcodeQuestionText">Αλλάξατε τον κωδικό. Θέλετε να ενημερώσετε και τον γραμμοκώδικα στην ίδια τιμή;</string>
|
||||
<string name="options">Επιλογές</string>
|
||||
<string name="noGroupCards">Αυτή η ομάδα είναι άδεια</string>
|
||||
<string name="settings_display_barcode_max_brightness">Επιπλέον φωτισμός οθόνης</string>
|
||||
<string name="group_name_is_empty">Το όνομα της ομάδας δεν πρέπει να είναι κενό</string>
|
||||
<string name="settings_display_barcode_max_brightness">Επιπλέον φωτισμός γραμμοκώδικα</string>
|
||||
<string name="group_name_is_empty">Το όνομα ομάδας δεν γίνεται να είναι κενό</string>
|
||||
<string name="group_edit">Επεξεργασία ομάδας</string>
|
||||
<string name="star">Προσθήκη στα αγαπημένα</string>
|
||||
<string name="unstar">Αφαίρεση από τα αγαπημένα</string>
|
||||
@@ -113,7 +114,10 @@
|
||||
<string name="currency">Νόμισμα</string>
|
||||
<string name="privacy_policy">Πολιτική απορρήτου</string>
|
||||
<string name="chooseImportType">Εισαγωγή δεδομένων από</string>
|
||||
<string name="app_loyalty_card_keychain">Lοyalty Card Keychain</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="privacy_policy_popup_text">Σημείωμα πολιτικής απορρήτου ( υποχρεωτικό σε κάποια \"μαγαζιά\" εφαρμογών)
|
||||
\n
|
||||
\nΜΗΔΕΝΙΚΆ ΔΕΔΟΜΈΝΑ ΣΥΛΛΈΓΟΝΤΑΙ, ο οποιοσδήποτε μπορεί να το επιβεβαιώσει μιας και η εφαρμογή είναι ελεύθερο λογισμικό.</string>
|
||||
<plurals name="groupCardCountWithArchived">
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> κάρτα ( <xliff:g id="archivedCount">%2$d</xliff:g> αρχειοθετήθηκε)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> κάρτες ( <xliff:g id="archivedCount">%2$d</xliff:g> αρχειοθετήθηκαν)</item>
|
||||
@@ -124,29 +128,30 @@
|
||||
\nΠάρτε το στέλνοντας email στο: support@stocardapp.com ζητώντας μια εξαγωγή αρχείων των δεδομένων σας.</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Θέλω να μοιραστώ μερικές κάρτες μαζί σου</string>
|
||||
<string name="editGroup">Επεξεργασία Ομάδας: <xliff:g>%s</xliff:g></string>
|
||||
<string name="setFrontImage">Επιλογή εμπρόσθιας εικόνας</string>
|
||||
<string name="setFrontImage">Επιλογή μπροστινής εικόνας</string>
|
||||
<string name="importVoucherVaultMessage">Επιλέξτε την <i>vouchervault.json</i> εξαγωγή από το Voucher Vault για εισαγωγή.
|
||||
\nΔημιουργήστε το επιλέγοντας Εξαγωγή στο Voucher Vault.</string>
|
||||
<string name="unsupportedBarcodeType">Ο τύπος γραμμωτού κώδικα δεν μπορεί να εμφανιστεί ακόμα. Μπορεί να υποστηρίζεται σε μια μελλοντική έκδοση της εφαρμογής.</string>
|
||||
<string name="frontImageDescription">Εμπρόσθια</string>
|
||||
<string name="unsupportedBarcodeType">Ο τύπος γραμμοκώδικα δεν γίνεται να εμφανιστεί ακόμα. Μπορεί να υποστηρίζεται σε μια μελλοντική έκδοση της εφαρμογής.</string>
|
||||
<string name="frontImageDescription">Μπροστινή εικόνα</string>
|
||||
<string name="photos">Φωτογραφίες</string>
|
||||
<string name="backImageDescription">Οπίσθια</string>
|
||||
<string name="updateBarcodeQuestionTitle">Ενημέρωση τιμής γραμμωτού κώδικα;</string>
|
||||
<string name="backImageDescription">Οπίσθια εικόνα</string>
|
||||
<string name="updateBarcodeQuestionTitle">Ενημέρωση τιμής γραμμοκώδικα;</string>
|
||||
<string name="passwordRequired">Παρακαλώ εισάγετε τον κωδικό</string>
|
||||
<string name="sort_by_most_recently_used">Χρήση</string>
|
||||
<string name="sort_by_most_recently_used">Χρησιμοποιήθηκαν πρόσφατα</string>
|
||||
<string name="shortcutSelectCard">Επιλέξτε μία κάρτα</string>
|
||||
<string name="barcodeImageDescriptionWithType">Εικόνα <xliff:g>%s</xliff:g> γραμμωτού κώδικα</string>
|
||||
<string name="barcodeImageDescriptionWithType">Εικόνα <xliff:g>%s</xliff:g> γραμμοκώδικα</string>
|
||||
<string name="app_libraries">Ελεύθερες βιβλιοθήκες τρίτων: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="license">Άδεια</string>
|
||||
<string name="include_if_asking_support">Αν θέλετε να ζητήσετε υποστήριξη, συμπεριλάβετε τις ακόλουθες πληροφορίες:</string>
|
||||
<string name="importSuccessful">Δεδομένα εισήχθησαν</string>
|
||||
<string name="moveUp">Προχώρα πάνω</string>
|
||||
<string name="barcodeType">Τύπος γραμμωτού κώδικα</string>
|
||||
<string name="barcodeType">Τύπος γραμμοκώδικα</string>
|
||||
<string name="app_resources">Ελεύθερες πηγές τρίτων: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="selectColor">Επιλογή χρώματος</string>
|
||||
<string name="setIcon">Ορισμός εικονιδίου</string>
|
||||
<string name="settings_sky_blue_theme">Γαλάζιο</string>
|
||||
<string name="settings_green_theme">Πράσινο</string>
|
||||
<string name="settings_grey_theme">Γκρι</string>
|
||||
<string name="settings_brown_theme">Καφέ</string>
|
||||
<string name="sort_by_expiry">Λήξη</string>
|
||||
<plurals name="groupCardCount">
|
||||
@@ -157,7 +162,7 @@
|
||||
<string name="exportSuccessful">Δεδομένα εξήχθησαν</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Αποτροπή κλειδώματος οθόνης</string>
|
||||
<string name="failedLaunchingPhotoPicker">Δεν βρέθηκε υποστηριζόμενη εφαρμογή συλλογής</string>
|
||||
<string name="noBarcode">Χωρίς γραμμωτό κώδικα</string>
|
||||
<string name="noBarcode">Χωρίς barcode</string>
|
||||
<string name="starImage">Αγαπημένο αστέρι</string>
|
||||
<string name="balanceSentence">Υπόλοιπο: <xliff:g>%s</xliff:g></string>
|
||||
<string name="failedParsingImportUriError">Δεν ήταν δυνατή η ανάλυση του εισαγόμενου URL</string>
|
||||
@@ -168,11 +173,11 @@
|
||||
<string name="app_copyright_old">Βασισμένο στο Loyalty Card Keychain
|
||||
\nπνευματικά δικαιώματα © 2016-2020 Branden Archer</string>
|
||||
<string name="settings_follow_system_orientation">Ακολούθηση συστήματος</string>
|
||||
<string name="settings_card_orientation">Προσανατολισμός οθόνης</string>
|
||||
<string name="settings_card_orientation">Προσανατολισμός γραμμοκώδικα</string>
|
||||
<string name="settings_portrait_orientation">Πορτραίτο</string>
|
||||
<string name="settings_landscape_orientation">Οριζόντια</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Πνευματικά δικαιώματα © 2019-<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="settings_lock_on_opening_orientation">Κλείδωμα τρέχοντος προσανατολισμού όταν ανοίγει μία κάρτα</string>
|
||||
<string name="settings_lock_on_opening_orientation">Κλείδωμα τωρινού προσανατολισμού όταν ανοίγει μία κάρτα</string>
|
||||
<string name="intent_import_card_from_url_share_text">Θέλω να μοιραστώ μία κάρτα μαζί σου</string>
|
||||
<string name="enter_group_name">Εισάγετε όνομα ομάδας</string>
|
||||
<string name="groups">Ομάδες</string>
|
||||
@@ -184,15 +189,15 @@
|
||||
<string name="leaveWithoutSaveConfirmation">Έξοδος χωρίς αποθήκευση;</string>
|
||||
<string name="expiryStateSentenceExpired">Έληξε: <xliff:g>%s</xliff:g></string>
|
||||
<string name="card">Κάρτα</string>
|
||||
<string name="editBarcode">Επεξεργασία γραμμωτού κώδικα</string>
|
||||
<string name="editBarcode">Επεξεργασία γραμμοκώδικα</string>
|
||||
<string name="chooseExpiryDate">Επιλέξτε ημερομηνία λήξης</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Μετακίνηση του γραμμωτού κώδικα στο πάνω μέρος της οθόνης</string>
|
||||
<string name="noBarcodeFound">Δεν βρέθηκε γραμμωτός κώδικα</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Μετακίνηση του γραμμοκώδικα στο πάνω μέρος της οθόνης</string>
|
||||
<string name="noBarcodeFound">Δεν βρέθηκε γραμμοκώδικας</string>
|
||||
<string name="balance">Υπόλοιπο</string>
|
||||
<string name="importCatima">Εισαγωγή από Catima</string>
|
||||
<string name="importStocard">Εισαγωγή από Stocard</string>
|
||||
<string name="importVoucherVault">Εισαγωγή από Voucher Vault</string>
|
||||
<string name="sameAsCardId">Όπως ο κωδικός</string>
|
||||
<string name="sameAsCardId">Ίδιος κωδικός</string>
|
||||
<string name="exportPassword">Προσθέστε έναν κωδικό για προστασία της εξαγωγής (προαιρετικά)</string>
|
||||
<string name="exportPasswordHint">Εισαγωγή κωδικού</string>
|
||||
<string name="failedGeneratingShareURL">Δεν ήταν δυνατή η δημιουργία κοινοποιούμενου URL. Παρακαλώ αναφέρετε το.</string>
|
||||
@@ -202,7 +207,7 @@
|
||||
<string name="settings_oled_dark">Απόλυτο μαύρο φόντο για το μαύρο θέμα</string>
|
||||
<string name="settings_system_locale">Σύστημα</string>
|
||||
<string name="settings_theme_color">Χρώμα θέματος</string>
|
||||
<string name="settings_catima_theme">Κάτιμα</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>
|
||||
@@ -213,99 +218,30 @@
|
||||
<string name="and_data_usage">και δεδομένα χρήσης</string>
|
||||
<string name="rate_this_app">Βαθμολογήστε την εφαρμογή</string>
|
||||
<string name="duplicateCard">Αντίγραφο</string>
|
||||
<string name="archive">Αρχειοθέτηση</string>
|
||||
<string name="archive">Αρχειοθετήστε</string>
|
||||
<string name="archived">Η κάρτα αρχειοθετήθηκε</string>
|
||||
<string name="unarchived">Η κάρτα αφαιρέθηκε από το αρχείο</string>
|
||||
<string name="unarchive">Αφαίρεση από το αρχείο</string>
|
||||
<string name="updateBalanceTitle">Πόσα ξοδέψατε ή λάβατε;</string>
|
||||
<string name="updateBalanceTitle">Πόσα ξοδέψατε;</string>
|
||||
<string name="cameraPermissionDeniedTitle">Αδύνατη η πρόσβαση στην κάμερα</string>
|
||||
<string name="failedToRetrieveImageFile">Αποτυχία ανάκτησης αρχείου εικόνας</string>
|
||||
<string name="previousCard">Προηγούμενη</string>
|
||||
<string name="nextCard">Επόμενη</string>
|
||||
<string name="updateBalance">Ενημέρωση υπολοίπου</string>
|
||||
<string name="barcodeLongPressMessage">Μόνο εικόνες μπορούν να ανοιχτούν στην εφαρμογή φωτογραφιών</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Για να σκανάρετε γραμμωτούς κώδικες, θα χρειαστεί πρόσβαση στην κάμερα από το Catima. Πατήστε εδώ για να δώσετε πρόσβαση.</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Για να σκανάρετε γραμμοκώδικες, θα χρειαστεί πρόσβαση στην κάμερα από το Catima. Πατήστε εδώ για να δώσετε πρόσβαση.</string>
|
||||
<plurals name="viewArchivedCardsWithCount">
|
||||
<item quantity="one">Προβολή αρχείου (<xliff:g>%1$d</xliff:g> κάρτας)</item>
|
||||
<item quantity="other">Προβολή αρχείου (<xliff:g>%1$d</xliff:g> καρτών)</item>
|
||||
</plurals>
|
||||
<string name="importCards">Εισαγωγή καρτών</string>
|
||||
<string name="updateBalanceHint">Εισάγετε ποσό</string>
|
||||
<string name="currentBalanceSentence">Τωρινό υπόλοιπο <xliff:g>%s</xliff:g></string>
|
||||
<string name="newBalanceSentence">Νέο υπόλοιπο: <xliff:g>%s</xliff:g></string>
|
||||
<string name="failedToOpenUrl">Εγκαταστήστε έναν περιηγητή πρώτα</string>
|
||||
<string name="welcome">Καλώς ήρθατε στο Catima</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Απενεργοποιεί το κλείδωμα οθόνης κατά την προβολή μιας κάρτας</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Απαραίτητο για να δουλέψει σε κάποιους σαρωτές</string>
|
||||
<string name="cameraPermissionRequired">Δικαίωμα πρόσβασης στην κάμερα απαραίτητο γι\' αυτή την ενέργεια…</string>
|
||||
<string name="settings_allow_content_provider_read_title">Να επιτρέπεται σε άλλες εφαρμογές να έχουν πρόσβαση στα δεδομένα μου</string>
|
||||
<string name="app_copyright_short">Πνευματικά δικαιώματα © Sylvia van Os και συνεργάτες</string>
|
||||
<string name="height">Ύψος:</string>
|
||||
<string name="switchToFrontImage">Μετάβαση στην εμπρόσθια εικόνα</string>
|
||||
<string name="switchToBackImage">Μετάβαση στην οπίσθια εικόνα</string>
|
||||
<string name="switchToBarcode">Μετάβαση σε γραμμωτό κώδικα</string>
|
||||
<string name="validFromSentence">Ισχύει από: <xliff:g>%s</xliff:g></string>
|
||||
<string name="permissionReadCardsLabel">Διαβάστε τις κάρτες Catima</string>
|
||||
<string name="openBackImageInGalleryApp">Ανοίξτε την οπίσθια εικόνα στη συλλογή εικόνων</string>
|
||||
<string name="permissionReadCardsDescription">Διάβασε τις Κάρτες σου Catima και όλες τους τις λεπτομέρειες, συμπεριλαμβανομένων των σημειώσεων και των εικόνων</string>
|
||||
<string name="donate">Δωρεά</string>
|
||||
<string name="icon_header_click_text">Πατήστε παρατεταμένα για επεξεργασία του εικονιδίου</string>
|
||||
<string name="openFrontImageInGalleryApp">Ανοίξτε την εμπρόσθια εικόνα στη συλλογή εικόνων</string>
|
||||
<string name="storageReadPermissionRequired">Δικαίωμα ανάγνωσης του χώρου αποθήκευσης απαραίτητο για αυτήν την ενέργεια…</string>
|
||||
<string name="settings_follow_sensor_orientation">Πάντα σε περιστροφή (αγνοεί τις ρυθμίσεις του συστήματος)</string>
|
||||
<string name="validFromDate">Ισχύει από</string>
|
||||
<string name="anyDate">Οποιαδήποτε ημερομηνία</string>
|
||||
<string name="chooseValidFromDate">Επιλέξτε έγκυρη ημερομηνία από</string>
|
||||
<string name="setBarcodeHeight">Ρυθμίστε το ύψος του γραμμωτού κώδικα</string>
|
||||
<string name="show_name_below_image_thumbnail">Εμφάνιση ονόματος κάτω από το εικονίδιο</string>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="balanceParsingFailed">Μη έγκυρο υπόλοιπο</string>
|
||||
<string name="continue_">Συνέχεια</string>
|
||||
<string name="settings_category_title_privacy">Απόρρητο</string>
|
||||
<string name="addFromPdfFile">Επιλογή αρχείου PDF</string>
|
||||
<string name="add_manually_warning_message">Για ορισμένα καταστήματα, ο γραμμωτός κώδικας διαφέρει από τον αριθμό που αναγράφεται πάνω στην κάρτα. Εξαιτίας αυτού, η εισαγωγή γραμμωτού κώδικα χειροκίνητα ενδέχεται να μην λειτουργεί πάντα. Προτείνεται να σκανάρετε τον γραμμωτό κώδικα με χρήση της κάμερας. Επιθυμείτε να συνεχίσετε ;</string>
|
||||
<string name="amountParsingFailed">Μη έγκυρο ποσό</string>
|
||||
<string name="show_balance">Προβολή υπολοίπου</string>
|
||||
<string name="action_display_options">Επιλογές εμφάνισης</string>
|
||||
<string name="settings_category_title_cards">Εμφάνιση καρτών</string>
|
||||
<string name="settings_category_title_general">Γενικά</string>
|
||||
<string name="show_archived_cards">Προβολή αρχειοθετημένων καρτών</string>
|
||||
<string name="addWithoutBarcode">Προσθήκη κάρτας χωρίς γραμμωτό κώδικα</string>
|
||||
<string name="view_online">Προβολή διαδικτυακά</string>
|
||||
<string name="errorReadingFile">Δεν ήταν δυνατή η ανάγνωση του αρχείου</string>
|
||||
<string name="failedLaunchingFileManager">Δεν ήταν δυνατή η εύρεση υποστηριζόμενου διαχειριστή αρχείων</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Ποιους από τους γραμμωτούς κώδικες που βρέθηκαν θέλετε να χρησιμοποιήσετε;</string>
|
||||
<string name="pageWithNumber">Σελίδα <xliff:g>%d</xliff:g></string>
|
||||
<string name="spend">Δαπάνησε</string>
|
||||
<string name="receive">Λήψη</string>
|
||||
<string name="settings_keep_screen_on_summary">Απενεργοποιεί το χρονικό όριο της οθόνης κατά την προβολή μιας κάρτας</string>
|
||||
<string name="settings_oled_dark_summary">Μειώνει τη χρήση της μπαταρίας στις οθόνες OLED</string>
|
||||
<string name="show_note">Εμφάνιση σημείωσης</string>
|
||||
<string name="action_more_options">Περισσότερες επιλογές</string>
|
||||
<string name="enter_card_id">Εισάγετε τον κωδικό αριθμό ή το κείμενο στην κάρτα σας</string>
|
||||
<string name="show_validity">Εμφάνιση εγκυρότητας</string>
|
||||
<string name="add_a_card_in_a_different_way">Προσθέστε μια κάρτα με διαφορετικό τρόπο</string>
|
||||
<string name="card_id_must_not_be_empty">Ο κωδικός αριθμός της κάρτας δεν πρέπει να είναι κενός</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Οι εφαρμογές θα πρέπει ωστόσο να ζητήσουν άδεια για να τους δοθεί πρόσβαση</string>
|
||||
<string name="field_must_not_be_empty">Το πεδίο δεν πρέπει να είναι κενό</string>
|
||||
<string name="manually_enter_barcode_instructions">Εισάγετε τον κωδικό αριθμό ή το κείμενο στην κάρτα σας και πατήστε τον γραμμωτό κώδικα που μοιάζει με αυτόν της κάρτας σας.</string>
|
||||
<string name="add_manually_warning_title">Συνιστάται η σάρωση</string>
|
||||
<string name="noCameraFoundGuideText">Η συσκευή σας δεν φαίνεται να διαθέτει κάμερα. Αν έχει, δοκιμάστε να κάνετε επανεκκίνηση της συσκευής. Διαφορετικά, χρησιμοποιήστε το κουμπί Περισσότερες επιλογές παρακάτω για να προσθέσετε έναν γραμμωτό κώδικα με άλλο τρόπο.</string>
|
||||
<string name="importCancelled">Η εισαγωγή ακυρώθηκε</string>
|
||||
<string name="exportCancelled">Η εξαγωγή ακυρώθηκε</string>
|
||||
<string name="useFrontImage">Χρήση εμπρόσθιας εικόνας</string>
|
||||
<string name="useBackImage">Χρήση οπίσθιας εικόνας</string>
|
||||
<string name="settings_use_volume_keys_navigation">Εναλλαγή καρτών με τα κουμπιά έντασης</string>
|
||||
<string name="settings_use_volume_keys_navigation_summary">Χρησιμοποιήστε τα κουμπιά έντασης ήχου για να αλλάξετε την κάρτα που εμφανίζεται</string>
|
||||
<string name="settings_column_count_landscape">Στήλες σε οριζόντια λειτουργία</string>
|
||||
<string name="settings_column_count_1">1</string>
|
||||
<string name="settings_column_count_3">3</string>
|
||||
<string name="settings_column_count_5">5</string>
|
||||
<string name="settings_column_count_4">4</string>
|
||||
<string name="settings_category_title_cards_overview">Επισκόπηση καρτών</string>
|
||||
<string name="settings_column_count_portrait">Στήλες σε κατακόρυφη λειτουργία</string>
|
||||
<string name="settings_automatic_column_count">Αυτόματα</string>
|
||||
<string name="settings_column_count_2">2</string>
|
||||
<string name="settings_column_count_6">6</string>
|
||||
<string name="settings_column_count_7">7</string>
|
||||
<string name="generic_error_please_retry">Λυπούμαστε, κάτι πήγε στραβά, δοκιμάστε ξανά...</string>
|
||||
<string name="unsupportedFile">Το αρχείο δεν υποστηρίζεται</string>
|
||||
<string name="addFromPkpass">Επιλογή αρχείου Passbook (.pkpass)</string>
|
||||
<string name="sort_by_valid_from">Έναρξη ισχύος</string>
|
||||
</resources>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Απενεργοποιεί το κλείδωμα οθόνης ενόσω βλέπετε μια κάρτα</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Απαραίτητο για να δουλέψει σε κάποια σκάνερ</string>
|
||||
<string name="cameraPermissionRequired">Χρειάζεται άδεια για πρόσβαση στην κάμερα γι\' αυτή την ενέργεια…</string>
|
||||
<string name="settings_allow_content_provider_read_title">Επιτρέπει σε άλλες εφαρμογές να έχουν πρόσβαση στα δεδομένα μου</string>
|
||||
</resources>
|
||||
@@ -1,24 +1,24 @@
|
||||
<?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="delete">Forigi</string>
|
||||
<string name="noBarcode">Sen strikodo</string>
|
||||
<string name="barcodeType">Tipo de strikodo</string>
|
||||
<string name="noBarcode">Sen strekokodo</string>
|
||||
<string name="barcodeType">Tipo de strekokodo</string>
|
||||
<string name="cardId">Identigilo de karto</string>
|
||||
<string name="settings">Agordoj</string>
|
||||
<string name="selectBarcodeTitle">Elekti strikodon</string>
|
||||
<string name="selectBarcodeTitle">Elekti strekokodon</string>
|
||||
<string name="debug_version_fmt">Versio: <xliff:g id="version">%s</xliff:g></string>
|
||||
<string name="about_title_fmt">Pri <xliff:g id="app_name">%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Kopirajto © 2019–<xliff:g>%d</xliff:g> Sylvia van Os kaj kontribuantoj</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Kopirajto © 2019–<xliff:g>%d</xliff:g> Sylvia van Os.</string>
|
||||
<string name="importOptionFilesystemButton">El dosiersistemo</string>
|
||||
<string name="importOptionFilesystemTitle">Enporti el dosiersistemo</string>
|
||||
<string name="exportFailedTitle">Eksportado malsukcesis</string>
|
||||
<string name="exportSuccessfulTitle">Eksportado sukcesis</string>
|
||||
<string name="importFailedTitle">Importado malsukcesis</string>
|
||||
<string name="importSuccessfulTitle">Importado sukcesis</string>
|
||||
<string name="exporting">Eksportante…</string>
|
||||
<string name="importing">Importante…</string>
|
||||
<string name="exportFailedTitle">Elportado malsukcesis</string>
|
||||
<string name="exportSuccessfulTitle">Elportado sukcesis</string>
|
||||
<string name="importFailedTitle">Enportado malsukcesis</string>
|
||||
<string name="importSuccessfulTitle">Enportado sukcesis</string>
|
||||
<string name="exporting">Elportante…</string>
|
||||
<string name="importing">Enportante…</string>
|
||||
<string name="exportName">Elporti</string>
|
||||
<string name="importExport">Importi/eksporti</string>
|
||||
<string name="importExport">Enporti/elporti</string>
|
||||
<string name="addCardTitle">Aldoni karton</string>
|
||||
<string name="editCardTitle">Redakti karton</string>
|
||||
<string name="sendLabel">Sendi…</string>
|
||||
@@ -28,7 +28,7 @@
|
||||
<string name="photos">Fotoj</string>
|
||||
<string name="points">Poentoj</string>
|
||||
<string name="currency">Valuto</string>
|
||||
<string name="editBarcode">Redakti strikodon</string>
|
||||
<string name="editBarcode">Redakti strekokodon</string>
|
||||
<string name="card">Karto</string>
|
||||
<string name="never">Neniam</string>
|
||||
<string name="groupsList">Grupoj: <xliff:g>%s</xliff:g></string>
|
||||
@@ -50,246 +50,37 @@
|
||||
<string name="deleteConfirmation">Ĉu forigi ĉi tiun karton\?</string>
|
||||
<string name="deleteTitle">Forigi karton</string>
|
||||
<string name="settings_display_barcode_max_brightness">Heligi barcode vido</string>
|
||||
<string name="starImage">Stelmarko</string>
|
||||
<string name="thumbnailDescription">Bildeto</string>
|
||||
<string name="starImage">Preferata stelo</string>
|
||||
<string name="thumbnailDescription">Bildeto por karto</string>
|
||||
<string name="app_copyright_old">Bazita sur Lojaleco Karto Keychain
|
||||
\nkopirajto © 2016-2020 Branden Archer</string>
|
||||
<string name="importOptionApplicationButton">Uzi alian apon</string>
|
||||
<string name="importOptionApplicationExplanation">Uzi ajnan apon aŭ vian preferatan dosiermastrumilon por malfermi dosieron.</string>
|
||||
\nkopirajto © 2016-2020 Branden Archer.</string>
|
||||
<string name="importOptionApplicationButton">Uzi alian app</string>
|
||||
<string name="importOptionApplicationExplanation">Uzi ajna app aŭ via preferata dosiera # mana\? ero por malfermi dosieron.</string>
|
||||
<string name="importOptionApplicationTitle">Uzi alian app</string>
|
||||
<string name="importOptionFilesystemExplanation">Elektu specifa dosiero de la dosiersistemo.</string>
|
||||
<string name="exportOptionExplanation">La datumoj estos skribita al loko de via elekto.</string>
|
||||
<string name="exportFailed">Ne povis fari eksportadon</string>
|
||||
<string name="importFailed">Ne povis fari importadon</string>
|
||||
<string name="importExportHelp">Fari savkopion de viaj datumoj permesas movi ilin al alia aparato.</string>
|
||||
<string name="failedParsingImportUriError">Ne eblis analizi la URI de la importado</string>
|
||||
<string name="noCardExistsError">Ne eblis trovi tiun karton</string>
|
||||
<string name="noCardsMessage">Aldonu karton unue</string>
|
||||
<string name="exportFailed">Ne povis eksporti kartoj</string>
|
||||
<string name="importFailed">Ne povis importi kartoj</string>
|
||||
<string name="importExportHelp">Subtenanta supre vian kartoj permesas vin movi ilin al alia aparato.</string>
|
||||
<string name="failedParsingImportUriError">Ne eblis analizi la importado URI</string>
|
||||
<string name="noCardExistsError">Ne eblis trovi karto</string>
|
||||
<string name="noCardsMessage">Aldoni karto unua</string>
|
||||
<string name="cardShortcut">Karto Mallongirejo</string>
|
||||
<string name="scanCardBarcode">Skani strikodon</string>
|
||||
<string name="scanCardBarcode">Scintigrafio Barcode Card</string>
|
||||
<string name="share">Interŝanĝado</string>
|
||||
<string name="star">Aldoni al miaj markitaj</string>
|
||||
<string name="star">Aldoni al miaj plej ŝatataj</string>
|
||||
<string name="settings_keep_screen_on">Teni sur ekrano</string>
|
||||
<string name="importSuccessful">Datumoj importitaj</string>
|
||||
<string name="enter_group_name">Entajpu nomo de la grupo</string>
|
||||
<string name="noGroups">Klaki la + plus butonon por aldoni grupojn por enkategoriigo.</string>
|
||||
<string name="importSuccessful">Karto datumo importitaj</string>
|
||||
<string name="enter_group_name">Eniri nomo de la grupo</string>
|
||||
<string name="noGroups">Klaki la + plus butonon por aldoni grupoj por categorization unua.</string>
|
||||
<string name="all">Ĉiuj</string>
|
||||
<string name="intent_import_card_from_url_share_text">Mi deziras dividi karto kun vi</string>
|
||||
<string name="exportSuccessful">Datumoj eksportitaj</string>
|
||||
<string name="noGroupCards">Ĉi tiu grupo estas malplena</string>
|
||||
<string name="noGiftCards">Klavu la \"+\" butonon por aldoni karton, aŭ importu el la menuo \" ⋮\".</string>
|
||||
<string name="exportSuccessful">Karto datumo eksportita</string>
|
||||
<string name="noGroupCards">Ĉi tiu grupo ne enhavas ajnan kartoj</string>
|
||||
<string name="noGiftCards">Klavu la \"+\" butonon por aldoni karton, aŭ importu uzi menuo \" ⋮\".</string>
|
||||
<plurals name="selectedCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> elektita</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> elektitaj</item>
|
||||
<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>
|
||||
<string name="app_name">Catima</string>
|
||||
<string name="barcodeImageDescriptionWithType">Bildo <xliff:g>%s</xliff:g> strikodo</string>
|
||||
<string name="cameraPermissionRequired">Permeso atingi fotilon estas bezonata por ĉi tiu ago…</string>
|
||||
<string name="permissionReadCardsLabel">Legi Catima kartojn</string>
|
||||
<string name="noMatchingGiftCards">Neniu rezulto. Provu ŝanĝi vian serĉon.</string>
|
||||
<string name="noGiftCardsGroup">Kreu kartojn kaj poste atribuu ilin al la grupo ĉi tie.</string>
|
||||
<string name="storageReadPermissionRequired">Permeso legi stokadon estas bezonata por ĉi tiu ago…</string>
|
||||
<string name="unstar">Forigi de miaj markitaj</string>
|
||||
<plurals name="deleteCardsTitle">
|
||||
<item quantity="one">Forigi <xliff:g>%d</xliff:g> karton</item>
|
||||
<item quantity="other">Forigi <xliff:g>%d</xliff:g> kartojn</item>
|
||||
</plurals>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">Forigi ĉi tiun <xliff:g>%d</xliff:g> karton daŭre?</item>
|
||||
<item quantity="other">Forigi ĉi tiujn <xliff:g>%d</xliff:g> kartojn daŭre?</item>
|
||||
</plurals>
|
||||
<string name="expiryDate">Valida ĝis</string>
|
||||
<string name="balanceSentence">Saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="noBarcodeFound">Ne eblis trovi strikodon</string>
|
||||
<string name="balance">Saldo</string>
|
||||
<string name="moveBarcodeToTopOfScreen">Movi la strekodon al la supro de la ekrano</string>
|
||||
<string name="errorReadingImage">Ne eblis legi bildon</string>
|
||||
<string name="settings_brown_theme">Bruna</string>
|
||||
<string name="showMoreInfo">Montri informojn</string>
|
||||
<string name="on_github">sur GitHub</string>
|
||||
<string name="archive">Enarkivigi</string>
|
||||
<string name="duplicateCard">Duoblaĵo</string>
|
||||
<string name="unarchived">Karto elarkivigita</string>
|
||||
<string name="archived">Karto enarkivigita</string>
|
||||
<string name="failedLaunchingPhotoPicker">Ne povis trovi subtenatan galerian apon</string>
|
||||
<string name="failedToOpenUrl">Unue instalu retumilon</string>
|
||||
<string name="importCards">Importi kartojn</string>
|
||||
<string name="switchToBackImage">Ŝanĝi al malantaŭa bildo</string>
|
||||
<string name="setBarcodeHeight">Starigi altecon de strikodo</string>
|
||||
<string name="icon_header_click_text">Premi longe por redakti bildeton</string>
|
||||
<string name="show_validity">Montri validecon</string>
|
||||
<string name="addFromImage">Elekti bildon el la galerio</string>
|
||||
<string name="shortcutSelectCard">Elekti karton</string>
|
||||
<string name="chooseValidFromDate">Elekti startdaton de valideco</string>
|
||||
<string name="privacy_policy">Regularo pri privateco</string>
|
||||
<string name="chooseExpiryDate">Elektu findaton de valideco</string>
|
||||
<string name="group_updated">Grupo ĝisdatigita</string>
|
||||
<string name="leaveWithoutSaveTitle">Eliri</string>
|
||||
<string name="moveDown">Movi malsupren</string>
|
||||
<string name="leaveWithoutSaveConfirmation">Eliri sen konservi?</string>
|
||||
<string name="validFromDate">Valida ekde</string>
|
||||
<string name="accept">Akcepti</string>
|
||||
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
|
||||
<string name="settings_sky_blue_theme">Ĉielblua</string>
|
||||
<string name="unarchive">Elarkivigi</string>
|
||||
<string name="switchToBarcode">Ŝanĝi al strikodo</string>
|
||||
<string name="currentBalanceSentence">Nuna saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="height">Alteco:</string>
|
||||
<string name="settings_category_title_general">Ĝenerala</string>
|
||||
<string name="settings_category_title_privacy">Privateco</string>
|
||||
<plurals name="balancePoints">
|
||||
<item quantity="one"><xliff:g>%s</xliff:g> punkto</item>
|
||||
<item quantity="other"><xliff:g>%s</xliff:g> punktoj</item>
|
||||
</plurals>
|
||||
<string name="group_edit">Redakti grupon</string>
|
||||
<string name="updateBalanceTitle">Kiom vi elspezis aŭ ricevis?</string>
|
||||
<string name="updateBalanceHint">Entajpi kvanton</string>
|
||||
<string name="previousCard">Antaŭa</string>
|
||||
<string name="nextCard">Sekva</string>
|
||||
<string name="reverse">inversi la ordon</string>
|
||||
<string name="translate_platform">sur Weblate</string>
|
||||
<string name="failedToRetrieveImageFile">Malsukcesis ricevi bildodosieron</string>
|
||||
<string name="help_translate_this_app">Helpi traduki ĉi tiun apon</string>
|
||||
<string name="welcome">Bonvenon al Catima</string>
|
||||
<string name="donate">Doni</string>
|
||||
<string name="validFromSentence">Valida ekde: <xliff:g>%s</xliff:g></string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card_summary">Preventas ekranŝloson dum rigardo al karto</string>
|
||||
<string name="settings_allow_content_provider_read_summary">Apoj devos peti permeson por ricevi aliron</string>
|
||||
<plurals name="groupCardCount">
|
||||
<item quantity="one"><xliff:g>%d</xliff:g> karto</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> kartoj</item>
|
||||
</plurals>
|
||||
<string name="group_name_already_in_use">Gruponomo jam uzata</string>
|
||||
<string name="group_name_is_empty">Gruponomo devas ne esti malplena</string>
|
||||
<string name="deleteConfirmationGroup">Forigi grupon?</string>
|
||||
<string name="failedOpeningFileManager">Unue instalu dosiermastrumilon.</string>
|
||||
<string name="addManually">Entajpi la strikodon permane</string>
|
||||
<string name="editGroup">Redaktante grupon: <xliff:g>%s</xliff:g></string>
|
||||
<string name="expiryStateSentenceExpired">Eksvalidiĝis: <xliff:g>%s</xliff:g></string>
|
||||
<string name="balanceParsingFailed">Nevalida saldo</string>
|
||||
<string name="chooseImportType">Importi datumojn de</string>
|
||||
<string name="importCatima">Importi el Catima</string>
|
||||
<string name="settings_green_theme">Verda</string>
|
||||
<string name="updateBalance">Ĝisdatigi saldon</string>
|
||||
<string name="barcodeLongPressMessage">Nur bildoj povas esti malfermitaj en la galeria apo</string>
|
||||
<string name="sort_by_name">Nomo</string>
|
||||
<string name="sort_by_most_recently_used">Plej laste uzitaj</string>
|
||||
<string name="sort_by_expiry">Dato de eksvalidiĝo</string>
|
||||
<string name="sort_by">Ordigi laŭ</string>
|
||||
<string name="version_history">Versio historio</string>
|
||||
<string name="credits">Dankoj</string>
|
||||
<string name="license">Permesilo</string>
|
||||
<string name="and_data_usage">kaj datumuzado</string>
|
||||
<string name="rate_this_app">Taksi ĉi tiun apon</string>
|
||||
<string name="report_error">Raporti eraron</string>
|
||||
<string name="starred">Markitaj</string>
|
||||
<string name="newBalanceSentence">Nova saldo: <xliff:g>%s</xliff:g></string>
|
||||
<string name="anyDate">Iam ajn</string>
|
||||
<string name="switchToFrontImage">Ŝanĝi al antaŭa bildo</string>
|
||||
<string name="openFrontImageInGalleryApp">Malfermi la antaŭan bildon en galeria apo</string>
|
||||
<string name="openBackImageInGalleryApp">Malfermi la malantaŭan bildon en galeria apo</string>
|
||||
<string name="show_name_below_image_thumbnail">Montri nomon sub la bildeto</string>
|
||||
<string name="show_note">Montri noton</string>
|
||||
<string name="settings_category_title_cards">Kartoj</string>
|
||||
<string name="action_display_options">Agordoj de montrado</string>
|
||||
<string name="show_archived_cards">Montri arkivitajn kartojn</string>
|
||||
<string name="view_online">Vidi enrete</string>
|
||||
<string name="action_more_options">Pliaj opcioj</string>
|
||||
<string name="settings_keep_screen_on_summary">Malŝaltas tempolimon de la ekrano dum rigardo al karto</string>
|
||||
<string name="settings_disable_lockscreen_while_viewing_card">Preventi la ekranŝloson</string>
|
||||
<string name="settings_allow_content_provider_read_title">Permesi al aliaj apoj aliri miajn datumojn</string>
|
||||
<string name="moveUp">Movi supren</string>
|
||||
<string name="expiryStateSentence">Valida ĝis: <xliff:g>%s</xliff:g></string>
|
||||
<string name="app_contributors">Ebligita de: <xliff:g id="app_contributors">%s</xliff:g></string>
|
||||
<string name="sort">Ordigi</string>
|
||||
<string name="source_repository">Koda deponejo</string>
|
||||
<string name="on_google_play">sur Google Play</string>
|
||||
<string name="include_if_asking_support">Si vi volas peti subtenon, inkluzivu la sekvan informon:</string>
|
||||
<string name="options">Opcioj</string>
|
||||
<plurals name="groupCardCountWithArchived">
|
||||
<item quantity="one"><xliff:g>%1$d</xliff:g> karto (<xliff:g id="archivedCount">%2$d</xliff:g> archived)</item>
|
||||
<item quantity="other"><xliff:g>%1$d</xliff:g> kartoj (<xliff:g id="archivedCount">%2$d</xliff:g> archived)</item>
|
||||
</plurals>
|
||||
<string name="show_balance">Montri saldon</string>
|
||||
<string name="addWithoutBarcode">Aldoni karton sen strikodo</string>
|
||||
<string name="permissionReadCardsDescription">legi viajn Catima-kartojn kaj ĉiujn iliajn detalojn, inkluzive de notoj kaj bildoj</string>
|
||||
<string name="cameraPermissionDeniedTitle">Fotilo neatingebla</string>
|
||||
<string name="noCameraPermissionDirectToSystemSetting">Por skani strikodojn Catima bezonas atingorajton al via fotilo. Klaku ĉi tie por ŝanĝi viajn permesajn agordojn.</string>
|
||||
<string name="app_copyright_short">Kopirajto © Sylvia van Os kaj kontribuantoj</string>
|
||||
<string name="settings_card_orientation">Orientiĝo de strikodo</string>
|
||||
<string name="settings_follow_system_orientation">Laŭ la sistemo</string>
|
||||
<string name="settings_portrait_orientation">Vertikala</string>
|
||||
<string name="settings_landscape_orientation">Horizontala</string>
|
||||
<string name="settings_display_barcode_max_brightness_summary">Bezonata por ke iuj skaniloj funkciu</string>
|
||||
<string name="unsupportedBarcodeType">Ne eblas montri ĉi tiun strikodspecon. Ĝi eble estos subtenata en posta versio de la apo.</string>
|
||||
<string name="importVoucherVaultMessage">Elektu la <i>vouchervault.json</i> eksporton de Voucher Vault kiun vi volas importi.
|
||||
\nKreu ĝin unue elektante \"Export\" en Voucher Vault.</string>
|
||||
<string name="sameAsCardId">Sama kiel la identigo</string>
|
||||
<string name="setBarcodeId">Starigi la valoron de la strikodo</string>
|
||||
<string name="barcodeId">Valoro de la strikodo</string>
|
||||
<string name="passwordRequired">Bonvolu entajpi la pasvorton</string>
|
||||
<string name="exportPasswordHint">Entajpi pasvorton</string>
|
||||
<string name="settings_oled_dark_summary">Malpligrandigas baterian uzadon sur OLED-ekranoj</string>
|
||||
<string name="selectColor">Elekti koloron</string>
|
||||
<string name="setIcon">Starigi bildeton</string>
|
||||
<string name="settings_catima_theme">Catima</string>
|
||||
<string name="settings_pink_theme">Rozkolora</string>
|
||||
<string name="field_must_not_be_empty">Kampo devas ne esti malplena</string>
|
||||
<string name="manually_enter_barcode_instructions">Entajpu la identigilon aŭ tekston sur via karto kaj premu la strikodon kiu aspektas kiel tiu sur via karto.</string>
|
||||
<string name="importStocardMessage">Elektu la <i>***.zip</i> eksoporton de Stocard kiun vi volas importi.
|
||||
\nAkiru ĝin sendante retpoŝton al support@stocardapp.com petante eksporton de viaj datumoj.</string>
|
||||
<string name="turn_flashlight_off">Malŝalti poŝlampon</string>
|
||||
<string name="add_manually_warning_title">Skani estas rekomendata</string>
|
||||
<string name="continue_">Daŭrigi</string>
|
||||
<string name="settings_oled_dark">Pura nigra fono por malhela temo</string>
|
||||
<string name="importLoyaltyCardKeychain">Importi el Loyalty Card Keychain</string>
|
||||
<string name="importFidmeMessage">Elektu la <i>fidme-export-request-xxxxxx.zip</i> exporton de FidMe kiun vi volas importi kaj poste elektu la tipojn de strikodo permane.
|
||||
\nKreu ĝin en via FidMe profilo elektante \"Data Protection\" kaj poste \"Extract my data\".</string>
|
||||
<string name="backImageDescription">Malantaŭa bildo</string>
|
||||
<string name="noCameraFoundGuideText">Via aparato ŝajnas ne havi fotilon. Se jes, provu restartigi la aparaton. Alie, uzu la butonon \"Pliaj opcioj\" sube por aldoni strikodon alimaniere.</string>
|
||||
<string name="wrongValueForBarcodeType">La valoro ne validas por la elektita tipo de strikodo</string>
|
||||
<string name="importCancelled">Importado nuligita</string>
|
||||
<string name="exportCancelled">Eksportado nuligita</string>
|
||||
<string name="settings_theme_color">Koloro de la temo</string>
|
||||
<string name="app_libraries">Liberaj triaj bibliotekoj: <xliff:g id="app_libraries_list">%s</xliff:g></string>
|
||||
<string name="addFromPdfFile">Elekti PDF-dosieron</string>
|
||||
<string name="failedLaunchingFileManager">Subtenata dosiermastrumilo ne trovebla</string>
|
||||
<string name="errorReadingFile">Dosiero nelegebla</string>
|
||||
<string name="multipleBarcodesFoundPleaseChooseOne">Kiun el la trovitaj strikodoj vi volas uzi?</string>
|
||||
<string name="pageWithNumber">Paĝo <xliff:g>%d</xliff:g></string>
|
||||
<string name="settings_system_locale">Sistemo</string>
|
||||
<string name="app_resources">Liberaj triaj risurcoj: <xliff:g id="app_resources_list">%s</xliff:g></string>
|
||||
<string name="settings_follow_sensor_orientation">Ĉiam turni (ignori la agordojn de la sistemo)</string>
|
||||
<string name="settings_lock_on_opening_orientation">Fiksi al la orientiĝo uzata dum malfermado de la karto</string>
|
||||
<string name="importCatimaMessage">Elektu la <i>catima.zip</i> eksporton kiun vi volas importi.
|
||||
\nKreu ĝin unue en la importi/eksporti menuo en alia Catima apo elektante \'eksporti\' tie.</string>
|
||||
<string name="importFidme">Importi el FidMe</string>
|
||||
<string name="exportPassword">Starigi pasvorton por protekti vian eksporton (nedevige)</string>
|
||||
<string name="failedGeneratingShareURL">Komunigebla URL ne generebla. Bonvolu raporti.</string>
|
||||
<string name="intent_import_card_from_url_share_multiple_text">Mi volas kunhavigi kelkajn kartojn kun vi</string>
|
||||
<string name="frontImageDescription">Antaŭa bildo</string>
|
||||
<string name="setFrontImage">Starigi antaŭan bildon</string>
|
||||
<string name="setBackImage">Starigi malantaŭan bildon</string>
|
||||
<string name="removeImage">Forigi bildon</string>
|
||||
<string name="updateBarcodeQuestionTitle">Ĉu ĝisdatigi strikodvaloron?</string>
|
||||
<string name="updateBarcodeQuestionText">Vi ŝanĝis la identigon. Ĉu vi volas ankaŭ ĝisdatigi la strikodon por uzi la saman valoron?</string>
|
||||
<string name="importLoyaltyCardKeychainMessage">Elektu la <i>LoyaltyCardKeychain.csv</i> eksporton de Loyalty Card Keychain kiun vi volas importi.
|
||||
\nKreu ĝin unue de la \"Importi/eksporti\" menuo en Loyalty Card Keychain elektante \"eksporti\" tie.</string>
|
||||
<string name="importStocard">Importi de Stocard</string>
|
||||
<string name="importVoucherVault">Importi el Voucher Vault</string>
|
||||
<string name="turn_flashlight_on">Enŝalti poŝlampon</string>
|
||||
<string name="settings_locale">Lingvo</string>
|
||||
<string name="settings_magenta_theme">Maĝenta</string>
|
||||
<string name="settings_violet_theme">Viola</string>
|
||||
<string name="settings_blue_theme">Blua</string>
|
||||
<string name="enter_card_id">Entajpu la identigilon aŭ tekston sur via karto</string>
|
||||
<string name="card_id_must_not_be_empty">Identigilo devas ne esti malplena</string>
|
||||
<string name="add_a_card_in_a_different_way">Aldoni karton alimaniere</string>
|
||||
<string name="add_manually_warning_message">Ĉe kelkaj butikoj la strikodo malsamas la numero skribita sur la karto. Pro tio, enigi strikodon permane eble ne ĉiam funkcias. Estas forte rekomendite skani la strikodon per via fotilo anstataŭe. Ĉu vi ankoraŭ volas daŭrigi?</string>
|
||||
<string name="spend">Elspezi</string>
|
||||
<string name="receive">Ricevi</string>
|
||||
<string name="app_license">Rajtocedita libera softwaro, licencita laŭ GPLv3+</string>
|
||||
<string name="amountParsingFailed">Nevalida kvanto</string>
|
||||
<string name="useBackImage">Uzi la malantaŭan bildon</string>
|
||||
<string name="useFrontImage">Uzi la antaŭan bildon</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user