Compare commits

...

57 Commits

Author SHA1 Message Date
Sylvia van Os
2d4fa0fd85 Release Catima 2.25.3 2023-08-25 18:23:57 +02:00
Sylvia van Os
42863418a4 Merge pull request #1475 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-23 17:53:21 +02:00
Aya Ichrak
ac4ccf2635 Translated using Weblate (Arabic)
Currently translated at 92.9% (277 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ar/
2023-08-23 16:53:52 +02:00
Sylvia van Os
89762864ff Translated using Weblate (Arabic)
Currently translated at 92.6% (276 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ar/
2023-08-23 11:52:22 +02:00
ChaoticNeutralCzech
22b8f4b387 Translated using Weblate (Czech)
Currently translated at 100.0% (298 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2023-08-23 11:52:22 +02:00
Osama ALSHBIBI
aebb0e84dc Translated using Weblate (Arabic)
Currently translated at 92.6% (276 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ar/
2023-08-23 11:52:21 +02:00
IllusiveMan196
b75862532c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/uk/
2023-08-23 11:52:21 +02:00
Sylvia van Os
f30fa04d56 Add chat badge 2023-08-22 20:02:29 +02:00
Sylvia van Os
053b51f086 Merge pull request #1476 from CatimaLoyalty/create-pull-request/patch-1692514045
Update contributors
2023-08-20 08:50:28 +02:00
TheLastProject
f8960d9a1e Update contributors 2023-08-20 06:47:25 +00:00
Sylvia van Os
8949166ed1 Merge pull request #1473 from CatimaLoyalty/create-pull-request/patch-1691910455
Update contributors
2023-08-13 11:19:16 +02:00
TheLastProject
23c437580a Update contributors 2023-08-13 07:07:35 +00:00
Sylvia van Os
3e46e84b5d Merge pull request #1472 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-11 08:22:05 +02:00
solokot
3146e25a46 Translated using Weblate (Russian)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/ru/
2023-08-11 07:51:46 +02:00
Sylvia van Os
dc7b1b032b Merge pull request #1471 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-10 09:07:11 +02:00
Eric
18716fb333 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/zh_Hans/
2023-08-10 00:52:13 +02:00
Slávek Banko
5879b8716b Translated using Weblate (Czech)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2023-08-10 00:52:13 +02:00
Sylvia van Os
970e4b4a31 Merge pull request #1470 from CatimaLoyalty/dependabot/gradle/com.github.spotbugs-5.1.2
Bump com.github.spotbugs from 5.1.1 to 5.1.2
2023-08-09 08:12:10 +02:00
dependabot[bot]
b25e07d37a Bump com.github.spotbugs from 5.1.1 to 5.1.2
Bumps com.github.spotbugs from 5.1.1 to 5.1.2.

---
updated-dependencies:
- dependency-name: com.github.spotbugs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-09 02:17:50 +00:00
Sylvia van Os
c1041a09f5 Merge pull request #1469 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-08 18:07:33 +02:00
Clxff H3r4ld0
a4a70f44e0 Translated using Weblate (Indonesian)
Currently translated at 95.3% (122 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/id/
2023-08-08 17:52:18 +02:00
しいたけ
2e52e7b231 Translated using Weblate (Japanese)
Currently translated at 78.1% (233 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2023-08-08 17:52:18 +02:00
skauVictor
a7b1864c6b Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (298 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2023-08-08 17:52:18 +02:00
Sylvia van Os
ee3af751fe Merge pull request #1468 from CatimaLoyalty/create-pull-request/patch-1691305961
Update contributors
2023-08-06 09:35:54 +02:00
TheLastProject
34698c7bdd Update contributors 2023-08-06 07:12:41 +00:00
Sylvia van Os
a45875ef25 Merge pull request #1465 from CatimaLoyalty/create-pull-request/patch-1691229717
Update Fastlane changelogs
2023-08-05 12:02:09 +02:00
TheLastProject
c4df103c02 Update Fastlane changelogs 2023-08-05 10:01:57 +00:00
Sylvia van Os
44211accc9 Update CHANGELOG 2023-08-05 12:01:41 +02:00
Sylvia van Os
7be1ee99ca Merge pull request #1463 from obfusk/fix-colorpicker
LoyaltyCardEditActivity: fix color picker dialog crash & bug
2023-08-05 12:00:49 +02:00
FC Stegerman
b83dbb3a87 StocardImporter: refactor (#1443)
* StocardImporter: refactor

* StocardImporter: trim CSV fields

* LoyaltyCard: add .toString()

* StocardRecord: add .toString()

* StocardImporter: handle usages better

* StocardImporter: use label

* ImportExportTest: add importStocard2 + stocard2.zip

* StocardImporter: iterate over card map in key order

* StocardImporter: cleanup, handle label better, use providers file

* make spotbugs happy

* StocardImporter: can't use providersFileName, list known files, log unknown

* StocardImporter: fix regex, log properly, s/Provider/Store/

* StocardImporter: test /usages/ timestamp, nicer if/else flow

* StocardImporter: fix label usage

* StocardImporter: remove label prefix, improve error
2023-08-05 11:52:59 +02:00
FC Stegerman
7e3a5a9831 LoyaltyCardEditActivity: fix wrong balance parse after locale changes (#1454) 2023-08-05 11:29:59 +02:00
Sylvia van Os
1b2f939c5a Merge pull request #1462 from CatimaLoyalty/dependabot/gradle/com.github.spotbugs-5.1.1
Bump com.github.spotbugs from 5.1.0 to 5.1.1
2023-08-04 23:50:07 +02:00
FC Stegerman
29919851f5 LoyaltyCardEditActivity: fix color picker dialog crash & bug 2023-08-04 04:15:45 +02:00
dependabot[bot]
b255cd63de Bump com.github.spotbugs from 5.1.0 to 5.1.1
Bumps com.github.spotbugs from 5.1.0 to 5.1.1.

---
updated-dependencies:
- dependency-name: com.github.spotbugs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-04 02:13:47 +00:00
Sylvia van Os
5d022ee1d1 Merge pull request #1460 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-02 18:23:57 +02:00
Montazer Al-Taiee
ecd8fe6d43 Translated using Weblate (Arabic)
Currently translated at 92.6% (276 of 298 strings)

Translation: Catima/Android
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ar/
2023-08-02 15:07:57 +02:00
Eric
340046905d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/zh_Hans/
2023-08-02 15:07:57 +02:00
Slávek Banko
aba6dc9070 Translated using Weblate (Czech)
Currently translated at 100.0% (128 of 128 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2023-08-02 15:07:57 +02:00
Sylvia van Os
7e75f86aba Merge pull request #1459 from CatimaLoyalty/dependabot/gradle/com.github.spotbugs-5.1.0
Bump com.github.spotbugs from 5.0.14 to 5.1.0
2023-08-01 08:23:55 +02:00
Sylvia van Os
8c021141b0 Merge pull request #1458 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-08-01 08:23:19 +02:00
dependabot[bot]
1739ac827a Bump com.github.spotbugs from 5.0.14 to 5.1.0
Bumps com.github.spotbugs from 5.0.14 to 5.1.0.

---
updated-dependencies:
- dependency-name: com.github.spotbugs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 02:59:27 +00:00
Reza Almanda
2c8bbd3f44 Translated using Weblate (Indonesian)
Currently translated at 95.2% (121 of 127 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/id/
2023-08-01 03:16:37 +02:00
Slávek Banko
2afad63f31 Translated using Weblate (Czech)
Currently translated at 100.0% (127 of 127 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/cs/
2023-08-01 03:16:37 +02:00
Sylvia van Os
e4d2196892 Merge pull request #1457 from CatimaLoyalty/create-pull-request/patch-1690827013
Update Fastlane changelogs
2023-07-31 20:10:25 +02:00
TheLastProject
b26aced825 Update Fastlane changelogs 2023-07-31 18:10:12 +00:00
Sylvia van Os
a9625fc1cf Update CHANGELOG 2023-07-31 20:09:54 +02:00
Sylvia van Os
d86fb9475f Merge pull request #1452 from obfusk/fix-date-rotate
LoyaltyCardEditActivity: fix state loss on rotation for dates
2023-07-31 20:08:40 +02:00
Sylvia van Os
b6ea845236 Merge pull request #1456 from CatimaLoyalty/dependabot/gradle/com.google.zxing-core-3.5.2
Bump com.google.zxing:core from 3.5.1 to 3.5.2
2023-07-31 19:22:18 +02:00
dependabot[bot]
1e88e0c1cc Bump com.google.zxing:core from 3.5.1 to 3.5.2
Bumps [com.google.zxing:core](https://github.com/zxing/zxing) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/zxing/zxing/releases)
- [Changelog](https://github.com/zxing/zxing/blob/master/CHANGES)
- [Commits](https://github.com/zxing/zxing/compare/zxing-3.5.1...zxing-3.5.2)

---
updated-dependencies:
- dependency-name: com.google.zxing:core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 02:58:42 +00:00
Sylvia van Os
72b8781eec Merge pull request #1455 from weblate/weblate-catima-catima
Translations update from Hosted Weblate
2023-07-30 13:22:12 +02:00
Reza Almanda
dd8c63b088 Translated using Weblate (Indonesian)
Currently translated at 95.2% (121 of 127 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/id/
2023-07-30 13:08:02 +02:00
Eric
ec9affd8c3 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (127 of 127 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/zh_Hans/
2023-07-30 13:08:02 +02:00
Sylvia van Os
605e9711fa Translated using Weblate (Polish)
Currently translated at 36.2% (46 of 127 strings)

Translation: Catima/Android (Fastlane)
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/pl/
2023-07-30 13:08:02 +02:00
FC Stegerman
6dfbb169df LoyaltyCardEditActivity: fix noDataLossOnResumeOrRotate test 2023-07-29 21:30:08 +02:00
FC Stegerman
f671c6b0d1 LoyaltyCardEditActivity: fix state loss on rotation for dates 2023-07-29 21:14:51 +02:00
Sylvia van Os
b98ee46566 Merge pull request #1451 from obfusk/fix-divider
only show divider with name/note + extra field
2023-07-29 16:52:20 +02:00
FC Stegerman
3353cf288f only show divider with name/note + extra field 2023-07-29 15:49:09 +02:00
33 changed files with 467 additions and 256 deletions

View File

@@ -1,5 +1,12 @@
# Changelog
## v2.25.3 - 130 (2023-08-25)
- Minor UI fixes
- Fix valid from and expiry dates being reset when rotating the card editing screen
- Fix crash when rotating screen while the color picker is shown
- Stocard import fixes
## v2.25.2 - 129 (2023-07-27)
- Improved Catima importer (fixes cards missing when importing)

View File

@@ -19,8 +19,8 @@ android {
applicationId "me.hackerchick.catima"
minSdk 21
targetSdk 33
versionCode 129
versionName "2.25.2"
versionCode 130
versionName "2.25.3"
vectorDrawables.useSupportLibrary true
multiDexEnabled true
@@ -99,7 +99,7 @@ dependencies {
// Third-party
implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar'
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.google.zxing:core:3.5.2'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'net.lingala.zip4j:zip4j:2.11.5'

View File

@@ -8,6 +8,7 @@ import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class LoyaltyCard implements Parcelable {
@@ -166,6 +167,31 @@ public class LoyaltyCard implements Parcelable {
return id;
}
@NonNull
@Override
public String toString() {
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}",
this.id,
this.store,
this.note,
this.validFrom,
this.expiry,
this.balance,
this.balanceType,
this.cardId,
this.barcodeId,
this.barcodeType != null ? this.barcodeType.format() : null,
this.headerColor,
this.starStatus,
this.lastUsed,
this.zoomLevel,
this.archiveStatus
);
}
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
@Override
public LoyaltyCard createFromParcel(Parcel in) {

View File

@@ -176,39 +176,42 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
public void onBindViewHolder(LoyaltyCardListItemViewHolder inputHolder, Cursor inputCursor) {
// Invisible until we want to show something more
boolean showDivider = false;
inputHolder.mDivider.setVisibility(View.GONE);
LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(inputCursor);
Bitmap icon = Utils.retrieveCardImage(mContext, loyaltyCard.id, ImageLocationType.icon);
if (mShowNameBelowThumbnail && icon != null) {
showDivider = true;
inputHolder.setStoreField(loyaltyCard.store);
} else {
inputHolder.setStoreField(null);
}
if (mShowNote && !loyaltyCard.note.isEmpty()) {
showDivider = true;
inputHolder.setNoteField(loyaltyCard.note);
} else {
inputHolder.setNoteField(null);
}
if (mShowBalance && !loyaltyCard.balance.equals(new BigDecimal("0"))) {
inputHolder.setExtraField(inputHolder.mBalanceField, Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType), null);
inputHolder.setExtraField(inputHolder.mBalanceField, Utils.formatBalance(mContext, loyaltyCard.balance, loyaltyCard.balanceType), null, showDivider);
} else {
inputHolder.setExtraField(inputHolder.mBalanceField, null, null);
inputHolder.setExtraField(inputHolder.mBalanceField, null, null, false);
}
if (mShowValidity && loyaltyCard.validFrom != null) {
inputHolder.setExtraField(inputHolder.mValidFromField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.validFrom), Utils.isNotYetValid(loyaltyCard.validFrom) ? Color.RED : null);
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);
inputHolder.setExtraField(inputHolder.mValidFromField, null, null, false);
}
if (mShowValidity && loyaltyCard.expiry != null) {
inputHolder.setExtraField(inputHolder.mExpiryField, DateFormat.getDateInstance(DateFormat.LONG).format(loyaltyCard.expiry), Utils.hasExpired(loyaltyCard.expiry) ? Color.RED : null);
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);
inputHolder.setExtraField(inputHolder.mExpiryField, null, null, false);
}
inputHolder.mCardIcon.setContentDescription(loyaltyCard.store);
@@ -333,7 +336,7 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
});
}
private void setExtraField(TextView field, String text, Integer color) {
private void setExtraField(TextView field, String text, Integer color, boolean showDivider) {
// If text is null, hide the field
// If iconColor is null, use the default text and icon color based on theme
if (text == null) {
@@ -342,12 +345,15 @@ public class LoyaltyCardCursorAdapter extends BaseCursorAdapter<LoyaltyCardCurso
return;
}
field.setVisibility(View.VISIBLE);
// Shown when there is a name and/or note and at least 1 extra field
if (showDivider) {
mDivider.setVisibility(View.VISIBLE);
}
field.setText(text);
field.setTextColor(color != null ? color : MaterialColors.getColor(mContext, com.google.android.material.R.attr.colorSecondary, ContextCompat.getColor(mContext, mDarkModeEnabled ? R.color.md_theme_dark_secondary : R.color.md_theme_light_secondary)));
mDivider.setVisibility(View.VISIBLE);
field.setVisibility(View.VISIBLE);
Drawable icon = field.getCompoundDrawables()[0];
if (icon != null) {
icon.mutate();

View File

@@ -88,7 +88,7 @@ import protect.card_locker.async.TaskHandler;
import protect.card_locker.databinding.LayoutChipChoiceBinding;
import protect.card_locker.databinding.LoyaltyCardEditActivityBinding;
public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements BarcodeImageWriterResultCallback {
public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements BarcodeImageWriterResultCallback, ColorPickerDialogListener {
private LoyaltyCardEditActivityBinding binding;
private static final String TAG = "Catima";
@@ -174,6 +174,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
String tempStoredOldBarcodeValue = null;
boolean initDone = false;
boolean onResuming = false;
boolean onRestoring = false;
AlertDialog confirmExitDialog = null;
boolean validBalance = true;
@@ -231,7 +232,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
);
}
private void updateTempState(LoyaltyCardField fieldName, Object value) {
protected void updateTempState(LoyaltyCardField fieldName, Object value) {
tempLoyaltyCard = updateTempState(tempLoyaltyCard, fieldName, value);
if (initDone && (fieldName == LoyaltyCardField.cardId || fieldName == LoyaltyCardField.barcodeId || fieldName == LoyaltyCardField.barcodeType)) {
@@ -300,6 +301,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
@Override
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
onRestoring = true;
tempLoyaltyCard = savedInstanceState.getParcelable(STATE_TEMP_CARD);
super.onRestoreInstanceState(savedInstanceState);
tabs = binding.tabs;
@@ -383,9 +385,11 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
switch (textFieldToEdit) {
case validFrom:
formatDateField(this, validFromField, newDate);
updateTempState(LoyaltyCardField.validFrom, newDate);
break;
case expiry:
formatDateField(this, expiryField, newDate);
updateTempState(LoyaltyCardField.expiry, newDate);
break;
default:
throw new AssertionError("Unexpected field: " + textFieldToEdit);
@@ -393,7 +397,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
});
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
if (!hasFocus && !onResuming && !onRestoring) {
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(tempLoyaltyCard.balance, tempLoyaltyCard.balanceType));
}
});
@@ -401,6 +405,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
balanceField.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (onResuming || onRestoring) return;
try {
BigDecimal balance = Utils.parseBalance(s.toString(), tempLoyaltyCard.balanceType);
updateTempState(LoyaltyCardField.balance, balance);
@@ -425,7 +430,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
updateTempState(LoyaltyCardField.balanceType, currency);
if (tempLoyaltyCard.balance != null) {
if (tempLoyaltyCard.balance != null && !onResuming && !onRestoring) {
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(tempLoyaltyCard.balance, currency));
}
}
@@ -826,11 +831,19 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
noteFieldEdit.setText(tempLoyaltyCard.note);
formatDateField(this, validFromField, tempLoyaltyCard.validFrom);
formatDateField(this, expiryField, tempLoyaltyCard.expiry);
formatBalanceCurrencyField(tempLoyaltyCard.balanceType);
cardIdFieldView.setText(tempLoyaltyCard.cardId);
barcodeIdField.setText(tempLoyaltyCard.barcodeId != null ? tempLoyaltyCard.barcodeId : getString(R.string.sameAsCardId));
barcodeTypeField.setText(tempLoyaltyCard.barcodeType != null ? tempLoyaltyCard.barcodeType.prettyName() : getString(R.string.noBarcode));
// We set the balance here (with onResuming/onRestoring == true) to prevent formatBalanceCurrencyField() from setting it (via onTextChanged),
// which can cause issues when switching locale because it parses the balance and e.g. the decimal separator may have changed.
formatBalanceCurrencyField(tempLoyaltyCard.balanceType);
BigDecimal balance = tempLoyaltyCard.balance == null ? new BigDecimal("0") : tempLoyaltyCard.balance;
tempLoyaltyCard = updateTempState(tempLoyaltyCard, LoyaltyCardField.balance, balance);
balanceField.setText(Utils.formatBalanceWithoutCurrencySymbol(tempLoyaltyCard.balance, tempLoyaltyCard.balanceType));
validBalance = true;
Log.d(TAG, "Setting balance to " + balance);
if (groupsChips.getChildCount() == 0) {
List<Group> existingGroups = DBHelper.getGroups(mDatabase);
@@ -933,6 +946,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
}
onResuming = false;
onRestoring = false;
// Fake click on the edit icon to cause the set icon option to pop up if the icon was
// long-pressed in the view activity
@@ -979,6 +993,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().equals(getString(defaultOptionStringId))) {
dateField.setTag(null);
updateTempState(loyaltyCardField, null);
} else if (s.toString().equals(getString(chooseDateOptionStringId))) {
if (!lastValue.toString().equals(getString(chooseDateOptionStringId))) {
dateField.setText(lastValue);
@@ -992,8 +1007,6 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
loyaltyCardField == LoyaltyCardField.validFrom ? (Date) expiryField.getTag() : null);
datePickerFragment.show(getSupportFragmentManager(), "datePicker");
}
updateTempState(loyaltyCardField, dateField.getTag());
}
@Override
@@ -1248,31 +1261,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
}
ColorPickerDialog dialog = dialogBuilder.create();
dialog.setColorPickerDialogListener(new ColorPickerDialogListener() {
@Override
public void onColorSelected(int dialogId, int color) {
updateTempState(LoyaltyCardField.headerColor, color);
thumbnailEditIcon.setBackgroundColor(Utils.needsDarkForeground(color) ? Color.BLACK : Color.WHITE);
thumbnailEditIcon.setColorFilter(Utils.needsDarkForeground(color) ? Color.WHITE : Color.BLACK);
// Unset image if set
thumbnail.setTag(null);
generateIcon(storeFieldEdit.getText().toString());
}
@Override
public void onDialogDismissed(int dialogId) {
// Nothing to do, no change made
}
});
dialog.show(getSupportFragmentManager(), "color-picker-dialog");
setCardImage(targetView, null, false);
mIconRemoved = true;
mIconUnsaved = false;
return null;
});
}
@@ -1349,6 +1338,29 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
}
}
// ColorPickerDialogListener callback used by the ColorPickerDialog created in ChooseCardImage to set the thumbnail color
// We don't need to set or check the dialogId since it's only used for that single dialog
@Override
public void onColorSelected(int dialogId, int color) {
// Unset image if set
setCardImage(thumbnail, null, false);
mIconRemoved = true;
mIconUnsaved = false;
updateTempState(LoyaltyCardField.headerColor, color);
thumbnailEditIcon.setBackgroundColor(Utils.needsDarkForeground(color) ? Color.BLACK : Color.WHITE);
thumbnailEditIcon.setColorFilter(Utils.needsDarkForeground(color) ? Color.WHITE : Color.BLACK);
generateIcon(storeFieldEdit.getText().toString());
}
// ColorPickerDialogListener callback
@Override
public void onDialogDismissed(int dialogId) {
// Nothing to do, no change made
}
public static class DatePickerFragment extends DialogFragment
implements DatePickerDialog.OnDateSetListener {

View File

@@ -5,6 +5,8 @@ import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.zxing.BarcodeFormat;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
@@ -27,6 +29,7 @@ import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,12 +51,48 @@ import protect.card_locker.ZipUtils;
* A header is expected for the each table showing the names of the columns.
*/
public class StocardImporter implements Importer {
public static class ZIPData {
public final HashMap<String, HashMap<String, Object>> loyaltyCardHashMap;
public final HashMap<String, HashMap<String, Object>> providers;
public static class StocardProvider {
public String name = null;
public String barcodeFormat = null;
public Bitmap logo = null;
}
ZIPData(final HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, final HashMap<String, HashMap<String, Object>> providers) {
this.loyaltyCardHashMap = loyaltyCardHashMap;
public static class StocardRecord {
public String providerId = null;
public String store = null;
public String label = null;
public String note = null;
public String cardId = null;
public String barcodeType = null;
public Long lastUsed = null;
public Bitmap frontImage = null;
public Bitmap backImage = null;
@NonNull
@Override
public String toString() {
return String.format(
"StocardRecord{%n providerId=%s,%n store=%s,%n label=%s,%n note=%s,%n cardId=%s,%n"
+ " barcodeType=%s,%n lastUsed=%s,%n frontImage=%s,%n backImage=%s%n}",
this.providerId,
this.store,
this.label,
this.note,
this.cardId,
this.barcodeType,
this.lastUsed,
this.frontImage,
this.backImage
);
}
}
public static class ZIPData {
public final Map<String, StocardRecord> cards;
public final Map<String, StocardProvider> providers;
ZIPData(final Map<String, StocardRecord> cards, final Map<String, StocardProvider> providers) {
this.cards = cards;
this.providers = providers;
}
}
@@ -68,6 +107,8 @@ public class StocardImporter implements Importer {
}
}
public static final String PROVIDER_PREFIX = "/loyalty-card-providers/";
private static final String TAG = "Catima";
public void importData(Context context, SQLiteDatabase database, File inputFile, char[] password) throws IOException, FormatException, JSONException, ParseException {
@@ -77,11 +118,11 @@ public class StocardImporter implements Importer {
try {
for (CSVRecord record : parser) {
HashMap<String, Object> recordData = new HashMap<>();
recordData.put("name", record.get("name"));
recordData.put("barcodeFormat", record.get("barcodeFormat"));
StocardProvider provider = new StocardProvider();
provider.name = record.get("name").trim();
provider.barcodeFormat = record.get("barcodeFormat").trim();
zipData.providers.put(record.get("_id"), recordData);
zipData.providers.put(record.get("_id").trim(), provider);
}
parser.close();
@@ -95,7 +136,7 @@ public class StocardImporter implements Importer {
zipInputStream.close();
input.close();
if (zipData.loyaltyCardHashMap.keySet().size() == 0) {
if (zipData.cards.keySet().size() == 0) {
throw new FormatException("Couldn't find any loyalty cards in this Stocard export.");
}
@@ -103,14 +144,13 @@ public class StocardImporter implements Importer {
saveAndDeduplicate(context, database, importedData);
}
public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, JSONException {
HashMap<String, HashMap<String, Object>> loyaltyCardHashMap = zipData.loyaltyCardHashMap;
HashMap<String, HashMap<String, Object>> providers = zipData.providers;
public ZIPData importZIP(ZipInputStream zipInputStream, final ZIPData zipData) throws IOException, FormatException, JSONException {
Map<String, StocardRecord> cards = zipData.cards;
Map<String, StocardProvider> providers = zipData.providers;
String[] providersFileName = null;
String[] customProvidersBaseName = null;
String customProviderId = "";
String[] cardBaseName = null;
String customProviderId = "";
String cardName = "";
LocalFileHeader localFileHeader;
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
@@ -121,173 +161,182 @@ public class StocardImporter implements Importer {
continue;
}
if (providersFileName == null) {
providersFileName = new String[]{
"extracts",
nameParts[1],
"users",
nameParts[1],
"analytics-properties",
"content.json"
};
String userId = nameParts[1];
if (customProvidersBaseName == null) {
// FIXME: can we use the points-account/statement/content.json balance info somehow?
/*
Known files:
extracts/<user-UUID>/users/<user-UUID>/
analytics-properties/content.json
devices/<device-UUID>/
analytics-properties/content.json
content.json
ip-location-wifi/content.json
enabled-regions/<UUID>/content.json
loyalty-card-custom-providers/<provider-UUID>/content.json - custom providers
loyalty-cards/<card-UUID>/
card-linked-coupons/accounts/default/
content.json
user-coupons/<UUID>/content.json
content.json - card itself
images/back.png - back image (legacy)
images/back/back.jpg - back image
images/back/content.json
images/front.png - front image (legacy)
images/front/content.json
images/front/front.jpg - front image
notes/default/content.json - note
points-account/
content.json
statement/content.json
usages/<UUID>/content.json - timestamps
usage-statistics/content.json - timestamps
reward-program-balances/<UUID>/content.json
*/
customProvidersBaseName = new String[]{
"extracts",
nameParts[1],
userId,
"users",
nameParts[1],
userId,
"loyalty-card-custom-providers"
};
cardBaseName = new String[]{
"extracts",
nameParts[1],
userId,
"users",
nameParts[1],
userId,
"loyalty-cards"
};
}
if (startsWith(nameParts, customProvidersBaseName, 1)) {
// Extract providerId
customProviderId = nameParts[customProvidersBaseName.length].split("\\.", 2)[0];
customProviderId = nameParts[customProvidersBaseName.length];
StocardProvider provider = providers.get(customProviderId);
if (provider == null) {
provider = new StocardProvider();
providers.put(customProviderId, provider);
}
// Name file
if (fileName.endsWith(customProviderId + "/content.json")) {
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
providers = appendToHashMap(
providers,
customProviderId,
"name",
jsonObject.getString("name")
);
provider.name = jsonObject.getString("name");
} else if (fileName.endsWith("logo.png")) {
providers = appendToHashMap(
providers,
customProviderId,
"logo",
ZipUtils.readImage(zipInputStream)
);
provider.logo = ZipUtils.readImage(zipInputStream);
} else if (!fileName.endsWith("/")) {
Log.d(TAG, "Unknown or unused loyalty-card-custom-providers file " + fileName + ", skipping...");
}
}
if (startsWith(nameParts, cardBaseName, 1)) {
} else if (startsWith(nameParts, cardBaseName, 1)) {
// Extract cardName
cardName = nameParts[cardBaseName.length].split("\\.", 2)[0];
cardName = nameParts[cardBaseName.length];
StocardRecord record = cards.get(cardName);
if (record == null) {
record = new StocardRecord();
cards.put(cardName, record);
}
// This is the card itself
if (fileName.endsWith(cardName + "/content.json")) {
JSONObject jsonObject = ZipUtils.readJSON(zipInputStream);
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"cardId",
jsonObject.getString("input_id")
);
record.cardId = jsonObject.getString("input_id");
if (jsonObject.has("input_provider_name")) {
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"store",
jsonObject.getString("input_provider_name")
);
record.store = jsonObject.getString("input_provider_name");
}
if (jsonObject.has("label")) {
String label = jsonObject.getString("label");
if (!label.isBlank()) {
record.label = label;
}
}
// Provider ID can be either custom or not, extract whatever version is relevant
String customProviderPrefix = "/users/" + nameParts[1] + "/loyalty-card-custom-providers/";
String customProviderPrefix = "/users/" + userId + "/loyalty-card-custom-providers/";
String providerId = jsonObject
.getJSONObject("input_provider_reference")
.getString("identifier");
if (providerId.startsWith(customProviderPrefix)) {
providerId = providerId.substring(customProviderPrefix.length());
} else if (providerId.startsWith(PROVIDER_PREFIX)) {
providerId = providerId.substring(PROVIDER_PREFIX.length());
} else {
providerId = providerId.substring("/loyalty-card-providers/".length());
throw new FormatException("Unsupported provider ID format: " + providerId);
}
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"_providerId",
providerId
);
record.providerId = providerId;
if (jsonObject.has("input_barcode_format")) {
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"barcodeType",
jsonObject.getString("input_barcode_format")
);
record.barcodeType = jsonObject.getString("input_barcode_format");
}
} else if (fileName.endsWith("notes/default/content.json")) {
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"note",
ZipUtils.readJSON(zipInputStream)
.getString("content")
);
record.note = ZipUtils.readJSON(zipInputStream).getString("content");
} else if (fileName.endsWith("usage-statistics/content.json")) {
JSONArray usages = ZipUtils.readJSON(zipInputStream).getJSONArray("usages");
if (usages.length() > 0) {
JSONObject lastUsedObject = usages.getJSONObject(usages.length() - 1);
for (int i = 0; i < usages.length(); i++) {
JSONObject lastUsedObject = usages.getJSONObject(i);
String lastUsedString = lastUsedObject.getJSONObject("time").getString("value");
long timeStamp = Instant.parse(lastUsedString).getEpochSecond();
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"lastUsed",
timeStamp
);
if (record.lastUsed == null || timeStamp > record.lastUsed) {
record.lastUsed = timeStamp;
}
}
} else if (fileName.matches(".*/usages/[^/]+/content.json")) {
JSONObject lastUsedObject = ZipUtils.readJSON(zipInputStream);
String lastUsedString = lastUsedObject.getJSONObject("time").getString("value");
long timeStamp = Instant.parse(lastUsedString).getEpochSecond();
if (record.lastUsed == null || timeStamp > record.lastUsed) {
record.lastUsed = timeStamp;
}
} else if (fileName.endsWith("/images/front.png") || fileName.endsWith("/images/front/front.jpg")) {
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"frontImage",
ZipUtils.readImage(zipInputStream)
);
record.frontImage = ZipUtils.readImage(zipInputStream);
} else if (fileName.endsWith("/images/back.png") || fileName.endsWith("/images/back/back.jpg")) {
loyaltyCardHashMap = appendToHashMap(
loyaltyCardHashMap,
cardName,
"backImage",
ZipUtils.readImage(zipInputStream)
);
record.backImage = ZipUtils.readImage(zipInputStream);
} else if (!fileName.endsWith("/")) {
Log.d(TAG, "Unknown or unused loyalty-cards file " + fileName + ", skipping...");
}
} else if (!fileName.endsWith("/")) {
Log.d(TAG, "Unknown or unused file " + fileName + ", skipping...");
}
}
return new ZIPData(loyaltyCardHashMap, providers);
return new ZIPData(cards, providers);
}
public ImportedData importLoyaltyCardHashMap(Context context, final ZIPData zipData) {
public ImportedData importLoyaltyCardHashMap(Context context, final ZIPData zipData) throws FormatException {
ImportedData importedData = new ImportedData(new ArrayList<>(), new HashMap<>());
int tempID = 0;
for (Map<String, Object> loyaltyCardData : zipData.loyaltyCardHashMap.values()) {
String providerId = (String) loyaltyCardData.get("_providerId");
List<String> cardKeys = new ArrayList<>(zipData.cards.keySet());
Collections.sort(cardKeys);
if (providerId == null) {
Log.d(TAG, "Missing providerId for card " + loyaltyCardData + ", ignoring...");
for (String key : cardKeys) {
StocardRecord record = zipData.cards.get(key);
if (record.providerId == null) {
Log.d(TAG, "Missing providerId for card " + record + ", ignoring...");
continue;
}
HashMap<String, Object> providerData = zipData.providers.get(providerId);
// Read store from card, if not available (old export), fall back to providerData
String store;
if (loyaltyCardData.containsKey("store")) {
store = (String) loyaltyCardData.get("store");
} else {
store = providerData != null ? providerData.get("name").toString() : providerId;
if (record.cardId == null) {
throw new FormatException("No card ID listed, but is required");
}
StocardProvider provider = zipData.providers.get(record.providerId);
// Read store from card, if not available (old export), fall back to providerData
String store = record.store != null ? record.store : provider != null ? provider.name : record.providerId;
String note = record.note != null ? record.note : "";
String barcodeTypeString = record.barcodeType != null ? record.barcodeType : provider != null ? provider.barcodeFormat : null;
if (record.label != null && !record.label.equals(store) && !record.label.equals(note)) {
note = note.isEmpty() ? record.label : note + "\n" + record.label;
}
String note = (String) Utils.mapGetOrDefault(loyaltyCardData, "note", "");
String cardId = (String) loyaltyCardData.get("cardId");
String barcodeTypeString = (String) Utils.mapGetOrDefault(loyaltyCardData, "barcodeType", providerData != null ? providerData.get("barcodeFormat") : null);
CatimaBarcode barcodeType = null;
if (barcodeTypeString != null && !barcodeTypeString.isEmpty()) {
if (barcodeTypeString.equals("RSS_DATABAR_EXPANDED")) {
@@ -300,32 +349,25 @@ public class StocardImporter implements Importer {
}
int headerColor = Utils.getRandomHeaderColor(context);
Bitmap cardIcon = null;
if (providerData != null && providerData.containsKey("logo")) {
cardIcon = (Bitmap) providerData.get("logo");
headerColor = Utils.getHeaderColorFromImage(cardIcon, headerColor);
if (provider != null && provider.logo != null) {
headerColor = Utils.getHeaderColorFromImage(provider.logo, headerColor);
}
long lastUsed;
if (loyaltyCardData.containsKey("lastUsed")) {
lastUsed = (long) loyaltyCardData.get("lastUsed");
} else {
lastUsed = Utils.getUnixTime();
}
long lastUsed = record.lastUsed != null ? record.lastUsed : Utils.getUnixTime();
LoyaltyCard card = new LoyaltyCard(tempID, store, note, null, null, BigDecimal.valueOf(0), null, cardId, null, barcodeType, headerColor, 0, lastUsed, DBHelper.DEFAULT_ZOOM_LEVEL, 0);
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<>();
if (cardIcon != null) {
images.put(ImageLocationType.icon, cardIcon);
if (provider != null && provider.logo != null) {
images.put(ImageLocationType.icon, provider.logo);
}
if (loyaltyCardData.containsKey("frontImage")) {
images.put(ImageLocationType.front, (Bitmap) loyaltyCardData.get("frontImage"));
if (record.frontImage != null) {
images.put(ImageLocationType.front, record.frontImage);
}
if (loyaltyCardData.containsKey("backImage")) {
images.put(ImageLocationType.back, (Bitmap) loyaltyCardData.get("backImage"));
if (record.backImage != null) {
images.put(ImageLocationType.back, record.backImage);
}
importedData.images.put(tempID, images);
@@ -361,16 +403,4 @@ public class StocardImporter implements Importer {
return true;
}
private HashMap<String, HashMap<String, Object>> appendToHashMap(HashMap<String, HashMap<String, Object>> loyaltyCardHashMap, String cardID, String key, Object value) {
HashMap<String, Object> loyaltyCardData = loyaltyCardHashMap.get(cardID);
if (loyaltyCardData == null) {
loyaltyCardData = new HashMap<>();
}
loyaltyCardData.put(key, value);
loyaltyCardHashMap.put(cardID, loyaltyCardData);
return loyaltyCardHashMap;
}
}

View File

@@ -8,9 +8,9 @@ Oğuz Ersen
Katharine Chui
mondstern
SlavekB
FC Stegerman
StoyanDimitrov
IllusiveMan196
FC Stegerman
Altonss
Michael Moroni
Gediminas Murauskas
@@ -19,11 +19,10 @@ Joel A
laralem
Taco
pfaffenrodt
gallegonovato
Eric
Nyatsuki
HudobniVolk
Samantaz Fox
Eric
arno-github
Ankit Tiwari
Sergio Paredes
@@ -63,7 +62,8 @@ K. Herbert
Lisa A.
Mawuena M. KODZO A.
Max
Still Hsu
Reza
Still / Azaka
String E. Fighter
Tapu
Yurical
@@ -84,6 +84,7 @@ Bottan Hermawan
zChiip
Clonewayx
D. Domig
Danylo Lystopadov
Diego
Eudes-alencar
Fede Pujol
@@ -92,6 +93,7 @@ francescbassas
Jason Li
Jean-Luc Tibaux
Jesse Davids
Kamborio
Kis Dominik
Lukas Grassauer
Luna Jernberg
@@ -148,6 +150,7 @@ Booc Sylvan
Brage Nesteby Reitan
Cap Amr Karam
Carlo Maria Cuoghi Barbagli
ChengCheng
CherryMonster222
Colgrave
Csaba
@@ -174,7 +177,6 @@ Jean-Baptiste
Kung-chih
Karvjorm
polar
Kamborio
krkk
Laura Ferraz
Lucas da Costa
@@ -191,6 +193,7 @@ Milan Šalka
Minecraft boom
Mobashir Raihan
Moi Toi
DiCeYMaYo
OPADILOP
DivideEtImpera
Nicolas
@@ -205,7 +208,6 @@ Poorva Patidar
Quang Trung
Quang Nguyen
Ratnesh
Reza
Rohan Babbar
Ronak Upadhyay
Rose Liverman
@@ -247,6 +249,7 @@ polarhun
pooyanazari
psa-jforestier
sergio
skauVictor
080502
Marcus
techwebpd
@@ -256,6 +259,7 @@ tygyh
unstartdev
wmilan 17
يوسف لطفي
しいたけ
元气
JaeBeom An
JungHee Lee

View File

@@ -288,4 +288,7 @@
<string name="chooseValidFromDate">اختر صالح من التاريخ</string>
<string name="validFromSentence">صالح من:<xliff:g>%s</xliff:g></string>
<string name="height">الطول:</string>
</resources>
<string name="permissionReadCardsDescription">اقرأ بطاقتك مع جميع التفاصيل، بما فيه الملاحضات والصور</string>
<string name="settings_display_barcode_max_brightness_summary">ظروري لعمل بعض الماسحات الضوئية</string>
<string name="permissionReadCardsLabel">اقرأ بطاقات كاتيما</string>
</resources>

View File

@@ -1,21 +1,21 @@
<?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>
<string name="cancel">Zrušit</string>
<string name="save">Uložit</string>
<string name="edit">Editovat</string>
<string name="edit">Upravit</string>
<string name="delete">Smazat</string>
<string name="confirm">Potvrdit</string>
<string name="ok">Ano</string>
<string name="ok">OK</string>
<string name="copy_to_clipboard">Kopírovat ID do schránky</string>
<string name="sendLabel">Odeslat…</string>
<string name="editCardTitle">Editovat kartu</string>
<string name="addCardTitle">Přidat kartu</string>
<string name="scanCardBarcode">Skenování čárového kódu</string>
<string name="scanCardBarcode">Naskenovat čárový kód</string>
<string name="noStoreError">Nezadáno žádné jméno</string>
<string name="noCardIdError">Nezadáno ID</string>
<string name="importExport">Import/Export</string>
@@ -29,10 +29,10 @@
<string name="exportFailed">Export nelze provést</string>
<string name="importing">Importuji…</string>
<string name="exporting">Exportuji…</string>
<string name="importOptionFilesystemTitle">Import ze souborového systému</string>
<string name="importOptionFilesystemTitle">Import z úložiště</string>
<string name="importOptionFilesystemExplanation">Vyberte konkrétní soubor v úložišti.</string>
<string name="importOptionFilesystemButton">Ze souborového systému</string>
<string name="importOptionApplicationTitle">Použitím jiné aplikace</string>
<string name="importOptionFilesystemButton">Z úložiště</string>
<string name="importOptionApplicationTitle">Přes jinou aplikaci</string>
<string name="importOptionApplicationExplanation">K otevření souboru použijte libovolnou aplikaci nebo svého oblíbeného správce souborů.</string>
<string name="importOptionApplicationButton">Použít jinou aplikaci</string>
<string name="about">O aplikaci</string>
@@ -53,8 +53,8 @@
<string name="app_copyright_old">Založeno na Loyalty Card Keychain
\ncopyright © 20162020 Branden Archer</string>
<string name="exportOptionExplanation">Data budou zapsána na místo podle vašeho výběru.</string>
<string name="failedParsingImportUriError">Nelze analyzovat importovanou URI</string>
<string name="noCardExistsError">Takovou kartu nelze najít</string>
<string name="failedParsingImportUriError">Nelze zpracovat URI importu</string>
<string name="noCardExistsError">Tuto kartu nelze najít</string>
<string name="noCardsMessage">Nejprve přidejte kartu</string>
<string name="cardShortcut">Zástupce karty</string>
<string name="share">Sdílet</string>
@@ -63,8 +63,8 @@
<string name="noBarcode">Žádný čárový kód</string>
<string name="barcodeNoBarcode">Tato karta nemá čárový kód</string>
<string name="barcodeType">Typ čárového kódu</string>
<string name="noMatchingGiftCards">Nic nenalezeno. Zkuste změnit vyhledávání.</string>
<string name="action_search">Vyhledávání</string>
<string name="noMatchingGiftCards">Nic nenalezeno. Zkuste zadat jiný výraz.</string>
<string name="action_search">Hledat</string>
<string name="thumbnailDescription">Miniatura</string>
<string name="card_ids_copied">ID zkopírováno</string>
<plurals name="deleteCardsConfirmation">
@@ -79,12 +79,12 @@
</plurals>
<string name="importSuccessful">Data importována</string>
<string name="intent_import_card_from_url_share_text">Chci s Vámi sdílet kartu</string>
<string name="settings_disable_lockscreen_while_viewing_card">Bránit uzamykání obrazovky</string>
<string name="settings_keep_screen_on">Udržovat obrazovku zapnutou</string>
<string name="settings_disable_lockscreen_while_viewing_card">Nezamykat obrazovku</string>
<string name="settings_keep_screen_on">Nevypínat obrazovku</string>
<string name="settings_dark_theme">Tmavý</string>
<string name="settings_light_theme">Světlý</string>
<string name="settings_system_theme">Podle systému</string>
<string name="settings_theme">Vzhled</string>
<string name="settings_theme">Motiv</string>
<string name="settings">Nastavení</string>
<string name="card">Karta</string>
<string name="balanceSentence">Zůstatek: <xliff:g>%s</xliff:g></string>
@@ -92,7 +92,7 @@
<string name="expiryStateSentence">Platí do: <xliff:g>%s</xliff:g></string>
<string name="moveDown">Přesunout dolů</string>
<string name="moveUp">Přesunout nahoru</string>
<string name="enterBarcodeInstructions">Zadejte ID a níže vyberte typ čárového kódu nebo „Tato karta nemá čárový kód“.</string>
<string name="enterBarcodeInstructions">Zadejte ID a níže vyberte typ čárového kódu nebo klikněte na „Tato karta nemá čárový kód“.</string>
<string name="settings_brown_theme">Hnědá</string>
<string name="settings_grey_theme">Šedá</string>
<string name="settings_green_theme">Zelená</string>
@@ -172,8 +172,8 @@
<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">Rozjasněné zobrazení čárového kódu</string>
<string name="starImage">Oblíbená hvězda</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>
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019<xliff:g>%d</xliff:g> Sylvia van Os</string>
<plurals name="selectedCardCount">
<item quantity="one">Vybrána <xliff:g>%d</xliff:g> karta</item>
@@ -223,9 +223,9 @@
</plurals>
<string name="settings_oled_dark">Čistě černé pozadí pro tmavý motiv</string>
<string name="include_if_asking_support">Pokud chcete požádat o podporu, uveďte následující informace:</string>
<string name="settings_follow_system_orientation">Následovat systém</string>
<string name="settings_follow_system_orientation">Podle orientace systému</string>
<string name="settings_portrait_orientation">Na výšku</string>
<string name="settings_lock_on_opening_orientation">Zamknout podle orientace použité při otevření karty</string>
<string name="settings_lock_on_opening_orientation">Ponechat orientaci jako při otevření karty</string>
<string name="archive">Archivovat</string>
<string name="unarchive">Vrátit z archivu</string>
<string name="archiveList">Archiv</string>
@@ -280,14 +280,14 @@
<string name="show_validity">Zobrazit platnost</string>
<string name="show_balance">Zobrazit zůstatek</string>
<string name="permissionReadCardsDescription">Číst vaše karty a všechny jejich podrobnosti, včetně poznámek a obrázků</string>
<string name="settings_allow_content_provider_read_summary">Aplikace stále budou muset požádat o povolení k poskytnutí přístupu</string>
<string name="settings_allow_content_provider_read_summary">Aplikace budou i tak muset požádat o povolení k poskytnutí přístupu</string>
<string name="permissionReadCardsLabel">Číst karty Catima</string>
<string name="settings_allow_content_provider_read_title">Umožnit ostatním aplikacím přístup k mým datům</string>
<string name="settings_keep_screen_on_summary">Zakáže časový limit obrazovky při prohlížení karty</string>
<string name="settings_keep_screen_on_summary">Při prohlížení karty vypnout časovač zhasnutí obrazovky</string>
<string name="settings_oled_dark_summary">Snižuje používání baterie na displejích OLED</string>
<string name="settings_category_title_privacy">Soukromí</string>
<string name="settings_display_barcode_max_brightness_summary">Nezbytné pro některé skenery, aby fungovaly</string>
<string name="settings_disable_lockscreen_while_viewing_card_summary">Zakáže zamčení obrazovky při prohlížení karty</string>
<string name="settings_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">Karty</string>
<string name="settings_category_title_general">Obecné</string>
</resources>

View File

@@ -204,4 +204,9 @@
<string name="group_name_is_empty">グループ名を入力してください</string>
<string name="shortcutSelectCard">カードを選択してください</string>
<string name="translate_platform">on Weblate</string>
</resources>
<string name="options">オプション</string>
<string name="show_note">メモを表示</string>
<string name="validFromDate">有効期限</string>
<string name="chooseValidFromDate">有効期限を選択</string>
<string name="anyDate">無期限</string>
</resources>

View File

@@ -283,4 +283,4 @@
<string name="settings_category_title_general">Generelt</string>
<string name="settings_category_title_privacy">Personvern</string>
<string name="settings_oled_dark_summary">Reduserer batteribruk for OLED-skjermer</string>
</resources>
</resources>

View File

@@ -1140,24 +1140,6 @@ public class ImportExportTest {
LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, 1);
assertEquals("GAMMA", card.store);
assertEquals("", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("55555", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1625600883, card.lastUsed);
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 2);
assertEquals("Air Miles", card.store);
assertEquals("szjsbs", card.note);
assertEquals(null, card.validFrom);
@@ -1170,8 +1152,26 @@ public class ImportExportTest {
assertEquals(0, card.starStatus);
assertEquals(1625690099, card.lastUsed);
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front)));
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back)));
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front)));
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back)));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 2);
assertEquals("GAMMA", card.store);
assertEquals("", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("55555", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1625600883, card.lastUsed);
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 3);
@@ -1195,6 +1195,91 @@ public class ImportExportTest {
TestHelpers.getEmptyDb(activity);
}
@Test
public void importStocard2() {
// Copy of stocard.zip, but with an extra card using a custom provider, a label for "Miles", and /usages/ timestamp
InputStream inputStream = getClass().getResourceAsStream("stocard2.zip");
// Import the Stocard data
ImportExportResult result = MultiFormatImporter.importData(activity.getApplicationContext(), mDatabase, inputStream, DataFormat.Stocard, null);
assertEquals(ImportExportResultType.Success, result.resultType());
assertEquals(4, DBHelper.getLoyaltyCardCount(mDatabase));
LoyaltyCard card = DBHelper.getLoyaltyCard(mDatabase, 1);
assertEquals("Foo", card.store);
assertEquals("", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("1234567895", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.ITF, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1624991439, card.lastUsed);
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.front));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.back));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 1, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 2);
assertEquals("Air Miles", card.store);
assertEquals("szjsbs\nMiles", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("7649484", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1625690099, card.lastUsed);
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-front.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.front)));
assertTrue(BitmapFactory.decodeStream(getClass().getResourceAsStream("stocard-back.jpg")).sameAs(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.back)));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 2, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 3);
assertEquals("GAMMA", card.store);
assertEquals("", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("55555", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.EAN_13, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1625600883, card.lastUsed);
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.front));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.back));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 3, ImageLocationType.icon));
card = DBHelper.getLoyaltyCard(mDatabase, 4);
assertEquals("", card.store);
assertEquals("", card.note);
assertEquals(null, card.validFrom);
assertEquals(null, card.expiry);
assertEquals(new BigDecimal("0"), card.balance);
assertEquals(null, card.balanceType);
assertEquals("(01)09010374000019(21)02097564604859211217(10)01231287693", card.cardId);
assertEquals(null, card.barcodeId);
assertEquals(BarcodeFormat.RSS_EXPANDED, card.barcodeType.format());
assertEquals(0, card.starStatus);
assertEquals(1625600120, card.lastUsed);
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.front));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.back));
assertNull(Utils.retrieveCardImage(activity.getApplicationContext(), 4, ImageLocationType.icon));
TestHelpers.getEmptyDb(activity);
}
@Test
public void importVoucherVault() {
InputStream inputStream = getClass().getResourceAsStream("vouchervault.json");

View File

@@ -405,7 +405,9 @@ public class LoyaltyCardViewActivityTest {
storeField.setText("correct store");
noteField.setText("correct note");
LoyaltyCardEditActivity.formatDateField(context, validFromField, validFromDate);
activity.updateTempState(LoyaltyCardField.validFrom, validFromDate);
LoyaltyCardEditActivity.formatDateField(context, expiryField, expiryDate);
activity.updateTempState(LoyaltyCardField.expiry, expiryDate);
balanceField.setText("100");
balanceTypeField.setText(currency.getSymbol());
cardIdField.setText("12345678");

View File

Binary file not shown.

View File

@@ -2,7 +2,7 @@
plugins {
id 'com.android.application' version '8.0.2' apply false
id 'com.github.spotbugs' version "5.0.14" apply false
id 'com.github.spotbugs' version "5.1.2" apply false
}
allprojects {

View File

@@ -10,6 +10,8 @@ Copylefted libre software (GPLv3+) card management app.
![Android CI](https://github.com/TheLastProject/Catima/workflows/Android%20CI/badge.svg)
[![Translation status](https://hosted.weblate.org/widgets/catima/-/svg-badge.svg)](https://hosted.weblate.org/engage/catima/)
[![Matrix](https://img.shields.io/matrix/catima%3Amatrix.org)](https://matrix.to/#/%23catima:matrix.org)
<a href="https://f-droid.org/repository/browse/?fdid=me.hackerchick.catima" target="_blank">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
<a href="https://apt.izzysoft.de/fdroid/index/apk/me.hackerchick.catima" target="_blank">

View File

@@ -1,2 +1,3 @@
- Vylepšení importu Catima (oprava chybějících karet při importu)
- Oprava havárie při otáčení obrazovky během nastavení data platnosti od/vypršení
- Drobná vylepšení uživatelského rozhraní

View File

@@ -0,0 +1,4 @@
- Drobná vylepšení uživatelského rozhraní
- Oprava vynulování data platnosti od/vypršení při otáčení obrazovky během editace karty
- Oprava havárie při otáčení obrazovky během zobrazeného výběru barvy
- Opravy importu Stocard

View File

@@ -0,0 +1,4 @@
- Minor UI fixes
- Fix valid from and expiry dates being reset when rotating the card editing screen
- Fix crash when rotating screen while the color picker is shown
- Stocard import fixes

View File

@@ -0,0 +1,3 @@
- Pengimpor Catima yang lebih baik (memperbaiki kartu yang hilang saat mengimpor)
- Memperbaiki kerusakan saat memutar layar saat mengatur tanggal valid dari / kedaluwarsa
- Perubahan kecil pada UI

View File

@@ -0,0 +1,2 @@
- Perbaikan UI kecil
- Memperbaiki tanggal valid dari dan tanggal kedaluwarsa yang diatur ulang saat memutar layar pengeditan kartu

View File

@@ -1,5 +1,5 @@
- Menambahkan dukungan untuk menambahkan pintasan ke layar beranda saat menambahkan atau mengedit kartu. (pull #155 (https://github.com/brarcher/loyalty-card-locker/pull/155))
- Widget dihapus karena pengganti pintasan yang buruk. (pull #155 (https://github.com/brarcher/loyalty-card-locker/pull/155))
- Perbaikan pada ekspor cadangan di Android 7+. (pull #153 (https://github.com/brarcher/loyalty-card-locker/pull/153))
- Menghapus widget, karena widget merupakan pengganti pintasan yang buruk. (pull #155 (https://github.com/brarcher/loyalty-card-locker/pull/155))
- Perbaiki ekspor cadangan di Android 7+. (pull #153 (https://github.com/brarcher/loyalty-card-locker/pull/153))
- Laporkan jenis mime yang lebih akurat saat mengekspor data cadangan. (pull #156 (https://github.com/brarcher/loyalty-card-locker/pull/156))
- Memperbaiki bug di mana kartu tidak dapat diedit. (pull #155 (https://github.com/brarcher/loyalty-card-locker/pull/155))

View File

@@ -1,7 +1,7 @@
- Menambahkan kemampuan untuk mencari kartu (#320 (https://github.com/brarcher/loyalty-card-locker/pull/320))
- Menambahkan kemampuan untuk berbagi dan menerima kartu loyalitas (#211 (https://github.com/brarcher/loyalty-card-locker/pull/321))
- Tambahkan kemampuan untuk berbagi dan menerima kartu loyalitas (#211 (https://github.com/brarcher/loyalty-card-locker/pull/321))
- Dukungan mode gelap (#322 (https://github.com/brarcher/loyalty-card-locker/pull/322))
- Kartu loyalitas sekarang dapat menjadi barcodeless (misalnya tidak memiliki barcode) (#324 (https://github.com/brarcher/loyalty-card-locker/pull/324))
- Kartu loyalitas sekarang dapat menjadi barcode(misalnya tidak memiliki barcode) (#324 (https://github.com/brarcher/loyalty-card-locker/pull/324))
- Catatan dapat menjangkau beberapa baris (#326 (https://github.com/brarcher/loyalty-card-locker/pull/326))
- Perbaikan dengan ukuran catatan (#319 (https://github.com/brarcher/loyalty-card-locker/pull/319))
- Meningkatkan visibilitas notifikasi dan ikon aplikasi (#330 (https://github.com/brarcher/loyalty-card-locker/pull/330))

View File

@@ -1,5 +1,5 @@
- BREAKING CHANGE: Format pencadangan berubah, lihat https://github.com/TheLastProject/Catima/wiki/Export-format
- BREAKING CHANGE: Format berbagi URL berubah, lihat https://github.com/TheLastProject/Catima/wiki/Card-sharing-URL-format
- PERUBAHAN TERBARU: Format pencadangan berubah, lihat https://github.com/TheLastProject/Catima/wiki/Export-format
- PERUBAHAN TERBARU: Format berbagi URL berubah, lihat https://github.com/TheLastProject/Catima/wiki/Card-sharing-URL-format
- Memungkinkan untuk mengaktifkan atau menonaktifkan senter saat memindai
- Menambahkan dukungan UPC-E
- Mendukung penambahan foto depan dan belakang ke setiap kartu

View File

@@ -1,4 +1,4 @@
- Kecerahan layar ditingkatkan hingga maksimum ketika menampilkan kartu, untuk membantu pemindai barcode berhasil menangkap barcode. (pull #54 (https://github.com/brarcher/loyalty-card-locker/pull/54))
- Menambahkan konfirmasi hapus saat menghapus kartu. (pull #55 (https://github.com/brarcher/loyalty-card-locker/pull/55))
- Menambahkan terjemahan untuk bahasa Jerman (pull #57 (https://github.com/brarcher/loyalty-card-locker/pull/57)) dan Ceko (pull #58 (https://github.com/brarcher/loyalty-card-locker/pull/58)).
- Tambahkan terjemahan untuk bahasa Jerman (pull #57 (https://github.com/brarcher/loyalty-card-locker/pull/57)) dan Ceko (pull #58 (https://github.com/brarcher/loyalty-card-locker/pull/58)).
- Perubahan klarifikasi untuk terjemahan bahasa Italia. (pull #66 (https://github.com/brarcher/loyalty-card-locker/pull/66))

View File

@@ -1,4 +1,4 @@
Bagian "Locker" dari nama itu tidak intuitif. Untuk membantu memperbaiki hal ini, sebuah ikon aplikasi baru telah dibuat oleh betsythefc yang lebih baik mewakili tujuan dari aplikasi ini: untuk menyimpan kartu loyalitas yang menggunakan barcode. Seiring dengan ikon baru ini, nama aplikasi telah diubah menjadi "Loyalty Card Keychain".
Bagian "Locker" dari nama itu tidak intuitif. Untuk membantu memperbaiki hal ini, sebuah ikon aplikasi baru telah dibuat oleh betsythefc yang lebih baik mewakili tujuan dari aplikasi ini: untuk menyimpan kartu loyalitas yang menggunakan barcode. Seiring dengan ikon baru ini, nama aplikasi telah diubah menjadi "Gantungan Kunci Kartu Loyalitas".
Fitur tambahan/peningkatan:

View File

@@ -1 +1 @@
Catima - Portfel na karty lojalnościowe
Catima

View File

@@ -1,2 +1,3 @@
- Улучшен импор в Catima (исправлено пропадание карт при импорте)
- Улучшен импорт в Catima (исправлено пропадание карт при импорте)
- Исправлен сбой при повороте экрана во время установки даты срока действия
- Незначительные изменения пользовательского интерфейса

View File

@@ -0,0 +1,4 @@
- Незначительные исправления пользовательского интерфейса
- Исправлена ошибка, при которой даты начала действия и окончания срока действия сбрасывались при повороте экрана во время редактирования карты
- Исправлена ошибка при вращении экрана во время отображения средства выбора цвета
- Исправления при импорте Stocard

View File

@@ -1,2 +1,3 @@
- Покращено імпортер Catima (виправлено пропущені картки під час імпорту)
- Виправлено збій при обертанні екрану під час встановлення дати терміну дії "від" та "до"
- Покращено імпортер Catima (виправлено зникання карт під час імпорту)
- Виправлено збій при обертанні екрану під час встановлення дат терміну дії
- Покращення інтерфейсу

View File

@@ -0,0 +1,4 @@
- Незначні правки інтерфейсу
- Виправлено скидання дійсних дат дій картки після обертання екрану
- Виправлено виліт програми після обертання екрану у режимі вибору кольору
- Виправлено імпорт з Stocard

View File

@@ -1,2 +1,3 @@
- 改进了 Catima 数据导入程序 (修复导入时的卡片丢失)
- 修复设置卡片有效期时屏幕旋转导致的崩溃
- 微调用户界面

View File

@@ -0,0 +1,4 @@
- 微小的用户界面修复
- 修复旋转卡片编辑屏幕时有效期被重置的问题
- 修复颜色选取工具显示状态下旋转屏幕造成的崩溃
- Stocard 导入问题修复