mirror of
https://github.com/CatimaLoyalty/Android.git
synced 2026-01-09 15:38:04 -05:00
Compare commits
47 Commits
revert-292
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d54323fde0 | ||
|
|
c02be50242 | ||
|
|
a883ce0f43 | ||
|
|
ace353d71d | ||
|
|
18dbb24375 | ||
|
|
78daf54716 | ||
|
|
ee6541ba54 | ||
|
|
e4e9fe05e1 | ||
|
|
9d4035c94e | ||
|
|
293d38bd09 | ||
|
|
28be05600b | ||
|
|
03649820ce | ||
|
|
93515d2f88 | ||
|
|
d7dc70c0df | ||
|
|
ce0738782a | ||
|
|
df738b9c1d | ||
|
|
8bf0033d22 | ||
|
|
01e08e4928 | ||
|
|
90aee54a3c | ||
|
|
b7c42b5c8c | ||
|
|
008b5254c6 | ||
|
|
1b1163a100 | ||
|
|
70c14514e6 | ||
|
|
5830813170 | ||
|
|
a07d4d1b8a | ||
|
|
29c4ccf4d9 | ||
|
|
417006cfa3 | ||
|
|
907a8578ca | ||
|
|
7e63babd47 | ||
|
|
c289bb0af3 | ||
|
|
80767e7cd6 | ||
|
|
90e6dd8738 | ||
|
|
d3de9c65e6 | ||
|
|
48993f0486 | ||
|
|
918c6fb41b | ||
|
|
d121841fdf | ||
|
|
95ace2aa39 | ||
|
|
b8cd52563d | ||
|
|
70754af167 | ||
|
|
80bad7ad56 | ||
|
|
e214d6d69e | ||
|
|
0e7509d966 | ||
|
|
58a5b16484 | ||
|
|
a8df588b33 | ||
|
|
ef948f16f1 | ||
|
|
11f942771f | ||
|
|
f507e15bfc |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,6 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased - 159
|
||||
## Unreleased - 162
|
||||
|
||||
- Fix list widget sometimes opening wrong card
|
||||
- Fix several bugs with shortcut handling
|
||||
- Fix About activity not using pure black title bar in OLED mode
|
||||
|
||||
## v2.41.4 - 161 (2026-01-04)
|
||||
|
||||
- Disable automatic barcode encoding detection for now (breaks too many cards)
|
||||
|
||||
## v2.41.3 - 160 (2026-01-04)
|
||||
|
||||
- Follow-up for fix in 2.41.2 for cards explicitly set to ISO-8859-1
|
||||
- Migrate old cards to ISO-8859-1 to fix sudden behaviour differences for existing cards
|
||||
|
||||
## v2.41.2 - 159 (2026-01-03)
|
||||
|
||||
- Fix change introduced in 2.41.0 that broke support for some scanners for non-UTF-8 barcodes
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ android {
|
||||
applicationId = "me.hackerchick.catima"
|
||||
minSdk = 21
|
||||
targetSdk = 36
|
||||
versionCode = 158
|
||||
versionName = "2.41.1"
|
||||
versionCode = 161
|
||||
versionName = "2.41.4"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled = true
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.zxing.common.StringUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -184,23 +185,44 @@ public class BarcodeImageWriterTask implements CompatCallable<Bitmap> {
|
||||
MultiFormatWriter writer = new MultiFormatWriter();
|
||||
|
||||
Map<EncodeHintType, Object> encodeHints = new ArrayMap<>();
|
||||
Charset chosenEncoding = encoding;
|
||||
// Use charset if defined or guess otherwise
|
||||
if (encoding != null) {
|
||||
Log.d(TAG, "Encoding explicitly set, " + encoding.name());
|
||||
encodeHints.put(EncodeHintType.CHARACTER_SET, encoding);
|
||||
if (chosenEncoding != null) {
|
||||
Log.d(TAG, "Encoding explicitly set, " + chosenEncoding.name());
|
||||
} else {
|
||||
String guessedEncoding = StringUtils.guessEncoding(cardId.getBytes(), new ArrayMap<>());
|
||||
Log.d(TAG, "Guessed encoding: " + guessedEncoding);
|
||||
|
||||
// We don't want to pass the guessed encoding as an encoding hint unless it is UTF-8 as
|
||||
// zxing is likely to add the mentioned encoding hint as ECI inside the barcode.
|
||||
// FIXME: Guessing encoding using zxing causes too many false positives and breaks the Deutschlandticket, a common public transport ticket in Germany
|
||||
// See https://github.com/CatimaLoyalty/Android/issues/2932
|
||||
//
|
||||
// Due to many barcode scanners in the wild being badly coded they may trip over ECI
|
||||
// info existing and fail to scan, such as in https://github.com/CatimaLoyalty/Android/issues/2921
|
||||
if (Objects.equals(guessedEncoding, "UTF8")) {
|
||||
Log.d(TAG, "Guessed encoding is UTF8, so passing as encoding hint");
|
||||
encodeHints.put(EncodeHintType.CHARACTER_SET, Charset.forName(guessedEncoding));
|
||||
}
|
||||
// So, for now, we just force ISO in the "guessing" path until we figure out a better way to guess
|
||||
// The previous code is commented before, DO NOT UNCOMMENT, IT IS BROKEN
|
||||
//
|
||||
// chosenEncoding = Charset.forName(StringUtils.guessEncoding(cardId.getBytes(), new ArrayMap<>()));
|
||||
// Log.d(TAG, "Guessed encoding: " + chosenEncoding.name());
|
||||
|
||||
// FIXME: Figure out a good way to automatically determine the best format to use, to not break UTF-8 barcodes
|
||||
// However, make sure to NOT break the Deutschlandticket!
|
||||
chosenEncoding = StandardCharsets.ISO_8859_1;
|
||||
Log.w(TAG, "The encoding guessing code path is temporarily disabled due to it breaking Deutschlandticket. Forcing ISO-8859-1...");
|
||||
}
|
||||
|
||||
// We don't want to pass the ISO-8859-1 as an encoding hint as zxing may add this as ECI
|
||||
// inside the barcode.
|
||||
//
|
||||
// Due to many barcode scanners in the wild being badly coded they may trip over ECI
|
||||
// info existing and fail to scan.
|
||||
// See:
|
||||
// - https://github.com/CatimaLoyalty/Android/issues/2921
|
||||
// - https://github.com/CatimaLoyalty/Android/issues/2932
|
||||
//
|
||||
// Just not always passing the encoding hint is slightly hacky, but in 5+ years of Catima
|
||||
// cards without encode hints have never caused any issues (unless they were UTF-8), yet
|
||||
// just days after passing ISO-8859-1 as CHARACTER_SET in the encode hints already 2
|
||||
// scan failures were reported (one for QR, one for Aztec).
|
||||
if (!Objects.equals(chosenEncoding.name(), StandardCharsets.ISO_8859_1.name())) {
|
||||
Log.d(TAG, "Chosen encoding is not ISO_8859_1, so passing as encoding hint");
|
||||
encodeHints.put(EncodeHintType.CHARACTER_SET, chosenEncoding);
|
||||
} else {
|
||||
Log.d(TAG, "Not passing encoding as encoding hint");
|
||||
}
|
||||
|
||||
BitMatrix bitMatrix;
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.Set;
|
||||
public class DBHelper extends SQLiteOpenHelper {
|
||||
public static final String DATABASE_NAME = "Catima.db";
|
||||
public static final int ORIGINAL_DATABASE_VERSION = 1;
|
||||
public static final int DATABASE_VERSION = 18;
|
||||
public static final int DATABASE_VERSION = 19;
|
||||
|
||||
// NB: changing these values requires a migration
|
||||
public static final int DEFAULT_ZOOM_LEVEL = 100;
|
||||
@@ -345,6 +345,17 @@ public class DBHelper extends SQLiteOpenHelper {
|
||||
db.execSQL("ALTER TABLE " + LoyaltyCardDbIds.TABLE
|
||||
+ " ADD COLUMN " + LoyaltyCardDbIds.BARCODE_ENCODING + " TEXT");
|
||||
}
|
||||
|
||||
// On upgrade to 2.41.3, force all existing "Automatic" cards to ISO-8859-1.
|
||||
// UTF-8 support was only added in 2.42.0, before that, all barcodes were saved without
|
||||
// any encoding info. As many scanners deal badly with ECI info, automatically guessing
|
||||
// UTF-8 for old barcodes may break them.
|
||||
//
|
||||
// New cards will be saved using either "Automatic" encoding to guess or, for pkpass files,
|
||||
// whatever encoding is specified.
|
||||
if (oldVersion < 19 && newVersion >= 19) {
|
||||
db.execSQL("UPDATE " + LoyaltyCardDbIds.TABLE + " SET " + LoyaltyCardDbIds.BARCODE_ENCODING + " = 'ISO-8859-1' WHERE " + LoyaltyCardDbIds.BARCODE_ENCODING + " IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> imageFiles(Context context, final SQLiteDatabase database) {
|
||||
|
||||
@@ -69,7 +69,9 @@ class ListWidget : AppWidgetProvider() {
|
||||
if (hasCards) {
|
||||
// If we have cards, create the list
|
||||
views = RemoteViews(context.packageName, R.layout.list_widget)
|
||||
val templateIntent = Intent(context, LoyaltyCardViewActivity::class.java)
|
||||
val templateIntent = Intent(context, LoyaltyCardViewActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
|
||||
@@ -1532,7 +1532,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
DBHelper.setLoyaltyCardGroups(mDatabase, viewModel.getLoyaltyCardId(), selectedGroups);
|
||||
|
||||
ShortcutHelper.updateShortcuts(this, DBHelper.getLoyaltyCard(this, mDatabase, viewModel.getLoyaltyCardId()));
|
||||
ShortcutHelper.updateShortcuts(this);
|
||||
|
||||
if (viewModel.getDuplicateFromLoyaltyCardId()) {
|
||||
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
|
||||
|
||||
@@ -794,7 +794,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
ShortcutHelper.updateShortcuts(this, loyaltyCard);
|
||||
ShortcutHelper.updateShortcuts(this);
|
||||
}
|
||||
|
||||
private void setStateBasedOnImageTypes() {
|
||||
@@ -896,7 +896,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);
|
||||
new ListWidget().updateAll(LoyaltyCardViewActivity.this);
|
||||
|
||||
// Re-init loyaltyCard with new data from DB
|
||||
@@ -922,7 +921,6 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
|
||||
|
||||
DBHelper.deleteLoyaltyCard(database, LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||
|
||||
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
|
||||
new ListWidget().updateAll(LoyaltyCardViewActivity.this);
|
||||
|
||||
finish();
|
||||
|
||||
@@ -156,8 +156,6 @@ class MainActivity : CatimaAppCompatActivity(), CardAdapterListener {
|
||||
Log.d(TAG, "Deleting card: " + loyaltyCard.id)
|
||||
|
||||
DBHelper.deleteLoyaltyCard(mDatabase, this@MainActivity, loyaltyCard.id)
|
||||
|
||||
ShortcutHelper.removeShortcut(this@MainActivity, loyaltyCard.id)
|
||||
}
|
||||
val tab = groupsTabLayout.getTabAt(selectedTab)
|
||||
mGroup = tab?.tag
|
||||
@@ -177,7 +175,6 @@ class MainActivity : CatimaAppCompatActivity(), CardAdapterListener {
|
||||
for (loyaltyCard in mAdapter.getSelectedItems()) {
|
||||
Log.d(TAG, "Archiving card: " + loyaltyCard.id)
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 1)
|
||||
ShortcutHelper.removeShortcut(this@MainActivity, loyaltyCard.id)
|
||||
updateLoyaltyCardList(false)
|
||||
inputMode.finish()
|
||||
invalidateOptionsMenu()
|
||||
@@ -466,6 +463,7 @@ class MainActivity : CatimaAppCompatActivity(), CardAdapterListener {
|
||||
}
|
||||
|
||||
ListWidget().updateAll(mAdapter.mContext)
|
||||
ShortcutHelper.updateShortcuts(mAdapter.mContext)
|
||||
}
|
||||
|
||||
private fun processParseResultList(
|
||||
|
||||
@@ -2,31 +2,35 @@ package protect.card_locker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
class ShortcutHelper {
|
||||
// Android documentation says that no more than 5 shortcuts
|
||||
// are supported. However, that may be too many, as not all
|
||||
// launcher will show all 5. Instead, the number is limited
|
||||
// to 3 here, so that the most recent shortcut has a good
|
||||
// chance of being shown.
|
||||
private static final int MAX_SHORTCUTS = 3;
|
||||
/**
|
||||
* This variable controls the maximum number of shortcuts available.
|
||||
* It is made public only to make testing easier and should not be
|
||||
* manually modified. We use -1 here as a default value to check if
|
||||
* the value has been set either manually by the test scenario or
|
||||
* automatically in the `updateShortcuts` function.
|
||||
* Its actual value will be set based on the maximum amount of shortcuts
|
||||
* declared by the launcher via `getMaxShortcutCountPerActivity`.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static int maxShortcuts = -1;
|
||||
|
||||
// https://developer.android.com/reference/android/graphics/drawable/AdaptiveIconDrawable.html
|
||||
private static final int ADAPTIVE_BITMAP_SCALE = 1;
|
||||
@@ -35,86 +39,42 @@ class ShortcutHelper {
|
||||
private static final int ADAPTIVE_BITMAP_IMAGE_SIZE = ADAPTIVE_BITMAP_VISIBLE_SIZE + 5 * ADAPTIVE_BITMAP_SCALE;
|
||||
|
||||
/**
|
||||
* Add a card to the app shortcuts, and maintain a list of the most
|
||||
* recently used cards. If there is already a shortcut for the card,
|
||||
* the card is marked as the most recently used card. If adding this
|
||||
* card exceeds the max number of shortcuts, then the least recently
|
||||
* used card shortcut is discarded.
|
||||
* Update the dynamic shortcut list with the most recently viewed cards
|
||||
* based on the lastUsed field. Archived cards are excluded from the shortcuts
|
||||
* list. The list keeps at most maxShortcuts number of elements.
|
||||
*/
|
||||
static void updateShortcuts(Context context, LoyaltyCard card) {
|
||||
if (card.archiveStatus == 1) {
|
||||
// Don't add archived card to menu
|
||||
return;
|
||||
static void updateShortcuts(Context context) {
|
||||
if (maxShortcuts == -1) {
|
||||
maxShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(context);
|
||||
}
|
||||
|
||||
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
|
||||
|
||||
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
||||
|
||||
String shortcutId = Integer.toString(card.id);
|
||||
|
||||
// Sort the shortcuts by rank, so working with the relative order will be easier.
|
||||
// This sorts so that the lowest rank is first.
|
||||
Collections.sort(list, Comparator.comparingInt(ShortcutInfoCompat::getRank));
|
||||
|
||||
Integer foundIndex = null;
|
||||
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
if (list.get(index).getId().equals(shortcutId)) {
|
||||
// Found the item already
|
||||
foundIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundIndex != null) {
|
||||
// If the item is already found, then the list needs to be
|
||||
// reordered, so that the selected item now has the lowest
|
||||
// rank, thus letting it survive longer.
|
||||
ShortcutInfoCompat found = list.remove(foundIndex.intValue());
|
||||
list.addFirst(found);
|
||||
} else {
|
||||
// The item is new to the list. We add it and trim the list later.
|
||||
ShortcutInfoCompat shortcut = createShortcutBuilder(context, card).build();
|
||||
list.addFirst(shortcut);
|
||||
}
|
||||
|
||||
LinkedList<ShortcutInfoCompat> finalList = new LinkedList<>();
|
||||
SQLiteDatabase database = new DBHelper(context).getReadableDatabase();
|
||||
Cursor loyaltyCardCursor = DBHelper.getLoyaltyCardCursor(
|
||||
database,
|
||||
"",
|
||||
null,
|
||||
DBHelper.LoyaltyCardOrder.LastUsed,
|
||||
DBHelper.LoyaltyCardOrderDirection.Ascending,
|
||||
DBHelper.LoyaltyCardArchiveFilter.Unarchived
|
||||
);
|
||||
|
||||
int rank = 0;
|
||||
|
||||
// The ranks are now updated; the order in the list is the rank.
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
ShortcutInfoCompat prevShortcut = list.get(index);
|
||||
while (rank < maxShortcuts && loyaltyCardCursor.moveToNext()) {
|
||||
int id = loyaltyCardCursor.getInt(loyaltyCardCursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(context, database, id);
|
||||
|
||||
LoyaltyCard loyaltyCard = DBHelper.getLoyaltyCard(context, database, Integer.parseInt(prevShortcut.getId()));
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(rank)
|
||||
.build();
|
||||
|
||||
// skip outdated cards that no longer exist
|
||||
if (loyaltyCard != null) {
|
||||
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
|
||||
.setRank(rank)
|
||||
.build();
|
||||
|
||||
finalList.addLast(updatedShortcut);
|
||||
rank++;
|
||||
|
||||
// trim the list
|
||||
if (rank >= MAX_SHORTCUTS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
finalList.addLast(updatedShortcut);
|
||||
rank++;
|
||||
}
|
||||
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, finalList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given card id from the app shortcuts, if such a
|
||||
* shortcut exists.
|
||||
*/
|
||||
static void removeShortcut(Context context, int cardId) {
|
||||
ShortcutManagerCompat.removeDynamicShortcuts(context, Collections.singletonList(Integer.toString(cardId)));
|
||||
}
|
||||
|
||||
static @NotNull
|
||||
Bitmap createAdaptiveBitmap(@NotNull Bitmap in, int paddingColor) {
|
||||
Bitmap ret = Bitmap.createBitmap(ADAPTIVE_BITMAP_SIZE, ADAPTIVE_BITMAP_SIZE, Bitmap.Config.ARGB_8888);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package protect.card_locker.compose
|
||||
|
||||
import androidx.activity.OnBackPressedDispatcher
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -8,18 +10,38 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import protect.card_locker.R
|
||||
import protect.card_locker.preferences.Settings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CatimaTopAppBar(title: String, onBackPressedDispatcher: OnBackPressedDispatcher?) {
|
||||
// Use pure black in OLED theme
|
||||
val context = LocalContext.current
|
||||
val settings = Settings(context)
|
||||
val isDarkMode = when (settings.theme) {
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> false
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> true
|
||||
else -> isSystemInDarkTheme()
|
||||
}
|
||||
|
||||
val appBarColors = if (isDarkMode && settings.oledDark) {
|
||||
TopAppBarDefaults.topAppBarColors().copy(containerColor = Color.Black)
|
||||
} else {
|
||||
TopAppBarDefaults.topAppBarColors()
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
modifier = Modifier.testTag("topbar_catima"),
|
||||
title = { Text(text = title) },
|
||||
colors = appBarColors,
|
||||
navigationIcon = {
|
||||
if (onBackPressedDispatcher != null) {
|
||||
IconButton(onClick = { onBackPressedDispatcher.onBackPressed() }) {
|
||||
|
||||
@@ -459,8 +459,11 @@ public class CatimaImporter implements Importer {
|
||||
barcodeType = CatimaBarcode.fromName(unparsedBarcodeType);
|
||||
}
|
||||
|
||||
// Barcode encoding information is only supported since Catima 2.41.0, so old exports will lack this field
|
||||
// Database migration 19 forcing ISO-8859-1 on all cards, so we should treat imports from old Catima versions the same and force ISO-8859-1 there too
|
||||
// This limits the risk of breaking cards when importing an old database backup
|
||||
Charset barcodeEncoding = null;
|
||||
String unparsedBarcodeEncoding = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING, record, "");
|
||||
String unparsedBarcodeEncoding = CSVHelpers.extractString(DBHelper.LoyaltyCardDbIds.BARCODE_ENCODING, record, "ISO-8859-1");
|
||||
if (!unparsedBarcodeEncoding.isEmpty()) {
|
||||
barcodeEncoding = Charset.forName(unparsedBarcodeEncoding);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ Heimen Stoffels
|
||||
Oğuz Ersen
|
||||
FC (Fay) Stegerman
|
||||
StoyanDimitrov
|
||||
大王叫我来巡山
|
||||
B o d o
|
||||
大王叫我来巡山
|
||||
SlavekB
|
||||
Katharine Chui
|
||||
mondstern
|
||||
@@ -18,12 +18,12 @@ Altonss
|
||||
Edgars Andersons
|
||||
Joel A
|
||||
Michael Moroni
|
||||
Liner Seven
|
||||
Priit Jõerüüt
|
||||
Liner Seven
|
||||
Eric
|
||||
Fjuro
|
||||
Максим Горпиніч
|
||||
GitSpoon
|
||||
Fjuro
|
||||
GM
|
||||
Petr Novák
|
||||
laralem
|
||||
@@ -33,14 +33,14 @@ pfaffenrodt
|
||||
Aayush Gupta
|
||||
Scrambled777
|
||||
josé m
|
||||
Nyatsuki
|
||||
ikanakova
|
||||
Vasilis
|
||||
ikanakova
|
||||
Nyatsuki
|
||||
Kachelkaiser
|
||||
Giovanni Donisi
|
||||
Milo Ivir
|
||||
Горпиніч Максим Олександрович
|
||||
HudobniVolk
|
||||
Горпиніч Максим Олександрович
|
||||
Jiri Grönroos
|
||||
Warder
|
||||
Samantaz Fox
|
||||
@@ -48,18 +48,19 @@ Balázs Meskó
|
||||
109247019824
|
||||
Feike Donia
|
||||
Arno-github
|
||||
Ankit Tiwari
|
||||
Cliff Heraldo
|
||||
Sergio Paredes
|
||||
Ankit Tiwari
|
||||
Jose Delvani
|
||||
Robin
|
||||
Milan Šalka
|
||||
mdvhimself
|
||||
Milan Šalka
|
||||
AMIR-G98
|
||||
Robin
|
||||
தமிழ்நேரம்
|
||||
huuhaa
|
||||
Skrripy
|
||||
Govindgopalyadav
|
||||
damjang
|
||||
Skrripy
|
||||
huuhaa
|
||||
waffshappen
|
||||
Marnick L'Eau
|
||||
ngocanhtve
|
||||
@@ -67,19 +68,18 @@ aradxxx
|
||||
StellarSand
|
||||
Quentin PAGÈS
|
||||
Projjal Moitra
|
||||
Aliaksandr Trush
|
||||
e-michalak
|
||||
JungHee Lee
|
||||
AMIR-G98
|
||||
hajertabbane
|
||||
inavleb
|
||||
Ziad OUALHADJ
|
||||
Aliaksandr Trush
|
||||
Denis Shilin
|
||||
Traductor
|
||||
Gideon
|
||||
Renko
|
||||
Ricky Tigg
|
||||
Robin Liu
|
||||
Ricky Tigg
|
||||
Renko
|
||||
Gideon
|
||||
Traductor
|
||||
Denis Shilin
|
||||
しいたけ
|
||||
Alexander Ivanov
|
||||
Miha Frangež
|
||||
@@ -88,13 +88,14 @@ mrestivill
|
||||
ehrt74
|
||||
Virginie
|
||||
Tim Trek
|
||||
Peter Dave Hello
|
||||
Alì Mortacci
|
||||
MisterCosta96
|
||||
arshbeerSingh
|
||||
Augustin LAVILLE
|
||||
Freddo espresso
|
||||
vasudev-cell
|
||||
Vasudev R.
|
||||
Kim Seohyun
|
||||
rudy3
|
||||
Michael Gangolf
|
||||
PRATHAMESH BHAGAT
|
||||
Peter Dave Hello
|
||||
|
||||
@@ -299,4 +299,7 @@
|
||||
<string name="copy_value">Копиране на стойността</string>
|
||||
<string name="copied_to_clipboard">Копирано</string>
|
||||
<string name="nothing_to_copy">Няма стойност</string>
|
||||
<string name="back">Назад</string>
|
||||
<string name="automatic">Автоматично</string>
|
||||
<string name="barcodeEncoding">Кодиране на щрихкода</string>
|
||||
</resources>
|
||||
|
||||
@@ -305,4 +305,7 @@
|
||||
<string name="copy_value">Kopírovat hodnotu</string>
|
||||
<string name="copied_to_clipboard">Zkopírováno do schránky</string>
|
||||
<string name="nothing_to_copy">Nenalezena žádná hodnota</string>
|
||||
<string name="barcodeEncoding">Kódování čárového kódu</string>
|
||||
<string name="automatic">Automatické</string>
|
||||
<string name="back">Zpět</string>
|
||||
</resources>
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<string name="importLoyaltyCardKeychain">Aus Loyalty Card Keychain importieren</string>
|
||||
<string name="importFidmeMessage">Wähle deinen „FidMe-Export“ zum Importieren und anschließend manuell die Barcodetypen aus.\nErstelle ihn aus deinem FidMe-Profil, indem du Datenschutz wählst und dann auf Meine Daten extrahieren drückst.</string>
|
||||
<string name="importFidme">Aus FidMe importieren</string>
|
||||
<string name="importCatimaMessage">Wähle deinen „Catima-Export“ zum Importieren aus.\nErstelle diesen durch das Drücken auf Export im Import/Export-Menü in einer anderen Catima-Anwendung.</string>
|
||||
<string name="importCatimaMessage">Wähle deinen „Catima-Export“ zum Importieren aus.\nExportiere diesen zuvor im Import/Export-Menü einer anderen Catima-Anwendung.</string>
|
||||
<string name="importCatima">Aus Catima importieren</string>
|
||||
<string name="setBarcodeId">Barcodewert festlegen</string>
|
||||
<string name="sameAsCardId">Entspricht Kartennummer</string>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<string name="edit">Editar</string>
|
||||
<string name="delete">Eliminar</string>
|
||||
<string name="confirm">Confirmar</string>
|
||||
<string name="ok">De acuerdo</string>
|
||||
<string name="ok">Aceptar</string>
|
||||
<string name="sendLabel">Enviar…</string>
|
||||
<string name="editCardTitle">Editar tarjeta</string>
|
||||
<string name="addCardTitle">Añadir tarjeta</string>
|
||||
@@ -134,7 +134,7 @@
|
||||
<item quantity="many"><xliff:g>%d</xliff:g> seleccionadas</item>
|
||||
<item quantity="other"><xliff:g>%d</xliff:g> seleccionadas</item>
|
||||
</plurals>
|
||||
<string name="deleteTitle">Eliminar la tarjeta</string>
|
||||
<string name="deleteTitle">Eliminar tarjeta</string>
|
||||
<string name="deleteConfirmation">¿Quiere eliminar permanentemente esta tarjeta\?</string>
|
||||
<plurals name="deleteCardsConfirmation">
|
||||
<item quantity="one">¿Borrar esta tarjeta <xliff:g>%d</xliff:g> permanentemente\?</item>
|
||||
@@ -305,4 +305,7 @@
|
||||
<string name="copy_value">Copia valor</string>
|
||||
<string name="copied_to_clipboard">Copiado al portapapeles</string>
|
||||
<string name="nothing_to_copy">Ningún valor encontrado</string>
|
||||
<string name="barcodeEncoding">Codificación de barra de código</string>
|
||||
<string name="automatic">Automático</string>
|
||||
<string name="back">Atrás</string>
|
||||
</resources>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<string name="privacy_policy">Privātuma politika</string>
|
||||
<string name="accept">Pieņemt</string>
|
||||
<string name="editGroup">Kopas labošana: <xliff:g>%s</xliff:g></string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Autortiesības © 2019–<xliff:g>%d</xliff:g> Sylvia van Os</string>
|
||||
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Autortiesības © 2019–<xliff:g>%d</xliff:g> Sylvia van Os un līdzdalībnieki</string>
|
||||
<string name="app_copyright_old">Balstīta uz Loyalty Card Keychain
|
||||
\nautortiesības © 2016–2020 Branden Archer</string>
|
||||
<string name="debug_version_fmt">Versija: <xliff:g id="version">%s</xliff:g></string>
|
||||
|
||||
@@ -497,7 +497,7 @@ public class DatabaseTest {
|
||||
assertEquals("cardId", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.UPC_A, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(null, card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
assertEquals(0, card.lastUsed);
|
||||
@@ -515,7 +515,7 @@ public class DatabaseTest {
|
||||
assertEquals("cardId", card2.cardId);
|
||||
assertEquals(null, card2.barcodeId);
|
||||
assertEquals(null, card2.barcodeType); // Empty string should've become null
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(null, card2.headerColor);
|
||||
assertEquals(0, card2.starStatus);
|
||||
assertEquals(0, card2.lastUsed);
|
||||
|
||||
@@ -669,7 +669,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertNull(card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
@@ -696,7 +696,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertNull(card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
@@ -735,7 +735,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(null, card.barcodeType);
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(1, (long) card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
@@ -762,7 +762,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(1, (long) card.headerColor);
|
||||
assertEquals(1, card.starStatus);
|
||||
|
||||
@@ -789,7 +789,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(1, (long) card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
@@ -823,7 +823,7 @@ public class ImportExportTest {
|
||||
assertEquals("12345", card.cardId);
|
||||
assertEquals(null, card.barcodeId);
|
||||
assertEquals(BarcodeFormat.AZTEC, card.barcodeType.format());
|
||||
assertEquals(null, card.barcodeEncoding);
|
||||
assertEquals(StandardCharsets.ISO_8859_1, card.barcodeEncoding); // Old cards are assumed to be ISO-8859-1
|
||||
assertEquals(1, (long) card.headerColor);
|
||||
assertEquals(0, card.starStatus);
|
||||
|
||||
|
||||
124
app/src/test/java/protect/card_locker/ShortcutHelperTest.java
Normal file
124
app/src/test/java/protect/card_locker/ShortcutHelperTest.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package protect.card_locker;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.android.controller.ActivityController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ShortcutHelperTest {
|
||||
private Activity mActivity;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private int id1;
|
||||
private int id2;
|
||||
private int id3;
|
||||
private int id4;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mActivity = Robolectric.setupActivity(MainActivity.class);
|
||||
mDatabase = TestHelpers.getEmptyDb(mActivity).getWritableDatabase();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
id1 = (int) DBHelper.insertLoyaltyCard(mDatabase, "store1", "note1", null, null, new BigDecimal("0"), null, "cardId1", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), null, Color.BLACK, 0, now,0);
|
||||
id2 = (int) DBHelper.insertLoyaltyCard(mDatabase, "store2", "note2", null, null, new BigDecimal("0"), null, "cardId2", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), null, Color.BLACK, 0, now + 10,0);
|
||||
id3 = (int) DBHelper.insertLoyaltyCard(mDatabase, "store3", "note3", null, null, new BigDecimal("0"), null, "cardId3", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), null, Color.BLACK, 0, now + 20,0);
|
||||
id4 = (int) DBHelper.insertLoyaltyCard(mDatabase, "store4", "note4", null, null, new BigDecimal("0"), null, "cardId4", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), null, Color.BLACK, 0, now + 30,0);
|
||||
|
||||
ShortcutHelper.maxShortcuts = 3;
|
||||
}
|
||||
|
||||
private Integer[] getShortcutIds(Context context) {
|
||||
return ShortcutManagerCompat.getDynamicShortcuts(context)
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(ShortcutInfoCompat::getRank))
|
||||
.map(shortcut -> Integer.parseInt(shortcut.getId()))
|
||||
.toArray(Integer[]::new);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onArchiveUnarchive() {
|
||||
ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create();
|
||||
|
||||
Activity mainActivity = (Activity) activityController.get();
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
assertEquals(3, ShortcutManagerCompat.getDynamicShortcuts(mainActivity).stream().count());
|
||||
|
||||
Integer[] ids = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id4, id3, id2}, ids);
|
||||
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, id4, 1);
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
Integer[] idsAfterArchive = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id3, id2, id1}, idsAfterArchive);
|
||||
|
||||
DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, id4, 0);
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
Integer[] idsAfterUnarchive = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id4, id3, id2}, idsAfterUnarchive);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAddRemoveFavorite() {
|
||||
ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create();
|
||||
|
||||
Activity mainActivity = (Activity) activityController.get();
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
assertEquals(3, ShortcutManagerCompat.getDynamicShortcuts(mainActivity).stream().count());
|
||||
|
||||
Integer[] ids = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id4, id3, id2}, ids);
|
||||
|
||||
DBHelper.updateLoyaltyCardStarStatus(mDatabase, id1, 1);
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
Integer[] idsAfterFav = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id1, id4, id3}, idsAfterFav);
|
||||
|
||||
DBHelper.updateLoyaltyCardStarStatus(mDatabase, id1, 0);
|
||||
|
||||
activityController.pause();
|
||||
activityController.resume();
|
||||
|
||||
Integer[] idsAfterUnfav = getShortcutIds(mainActivity);
|
||||
|
||||
assertArrayEquals(new Integer[] {id4, id3, id2}, idsAfterUnfav);
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
- Einstellung für die Spaltenanzahl funktioniert nun auch in der Kartenansicht für Gruppen
|
||||
- Unterstützung für Designfarben entfernt
|
||||
- Maximale Fotogröße reduziert, um Speicherplatz zu sparen (gilt nur für neu hinzugefügte Fotos)
|
||||
- AboutActivity von Android XML zu Jetpack Compose migriert
|
||||
- About Activity von Android XML zu Jetpack Compose migriert
|
||||
|
||||
2
fastlane/metadata/android/en-US/changelogs/160.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/160.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
- Follow-up for fix in 2.41.2 for cards explicitly set to ISO-8859-1
|
||||
- Migrate old cards to ISO-8859-1 to fix sudden behaviour differences for existing cards
|
||||
1
fastlane/metadata/android/en-US/changelogs/161.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/161.txt
Normal file
@@ -0,0 +1 @@
|
||||
- Disable automatic barcode encoding detection for now (breaks too many cards)
|
||||
3
fastlane/metadata/android/en-US/changelogs/162.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/162.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Fix list widget sometimes opening wrong card
|
||||
- Fix several bugs with shortcut handling
|
||||
- Fix About activity not using pure black title bar in OLED mode
|
||||
2
fastlane/metadata/android/pl-PL/changelogs/151.txt
Normal file
2
fastlane/metadata/android/pl-PL/changelogs/151.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
- Nowy projekt logo Catima
|
||||
- Aktualizacja tłumaczeń
|
||||
3
fastlane/metadata/android/pl-PL/changelogs/152.txt
Normal file
3
fastlane/metadata/android/pl-PL/changelogs/152.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Dodanie wsparcia dla plików .pkpasses
|
||||
- Usunięcie importera Stocard (Stocard już nie istnieje)
|
||||
- Tymczasowe wyłączenie widżetów dla Androida poniżej 12L (obejście problemu awarii)
|
||||
Reference in New Issue
Block a user