Compare commits

...

55 Commits

Author SHA1 Message Date
Sylvia van Os
225058ab2b Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-19 21:26:04 +02:00
Sylvia van Os
275dc6caa2 Release Catima 2.3.0 2021-08-19 21:25:35 +02:00
Sylvia van Os
ceb7b12154 Merge pull request #335 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-18 17:41:48 +02:00
Luna Jernberg
849da26198 Translated using Weblate (Swedish)
Currently translated at 99.4% (174 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-18 17:36:39 +02:00
solokot
5808c991bb Translated using Weblate (Russian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ru/
2021-08-18 17:36:39 +02:00
Heimen Stoffels
e8554cb221 Translated using Weblate (Dutch)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nl/
2021-08-18 17:36:39 +02:00
Allan Nordhøy
46092c060d Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.2% (158 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/nb_NO/
2021-08-18 17:36:38 +02:00
Gediminas Murauskas
736eebd45c Translated using Weblate (Lithuanian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/lt/
2021-08-18 17:36:38 +02:00
J. Lavoie
f72db53345 Translated using Weblate (Italian)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/it/
2021-08-18 17:36:38 +02:00
J. Lavoie
b8c017259e Translated using Weblate (French)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fr/
2021-08-18 17:36:38 +02:00
J. Lavoie
4b4a237b9a Translated using Weblate (German)
Currently translated at 100.0% (175 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/de/
2021-08-18 17:36:37 +02:00
Petr Novák
17034e992f Translated using Weblate (Czech)
Currently translated at 60.0% (105 of 175 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/cs/
2021-08-18 17:36:37 +02:00
Sylvia van Os
bf143038b0 Update CHANGELOG 2021-08-17 22:46:53 +02:00
Sylvia van Os
73805ad3bb Fix importing images from export 2021-08-17 22:46:03 +02:00
Sylvia van Os
7006e35ebf Remove pages managed in Catima-Website repository 2021-08-16 18:23:03 +02:00
Sylvia van Os
fc598018b2 Remove duplicate Privacy Policy
It is managed in https://github.com/TheLastProject/Catima-Website/ now
2021-08-16 18:21:36 +02:00
Sylvia van Os
d01ec85c32 Merge pull request #332 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-15 22:23:59 +02:00
Nyatsuki
04ee918152 Translated using Weblate (Japanese)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/ja/
2021-08-15 22:18:44 +02:00
Tymofii Lytvynenko
943b70647b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-15 22:18:44 +02:00
Sylvia van Os
edba5d5dca Add locale chooser (#304)
* Add locale chooser

* Prevent AppBundle crashes

* Reload activity on language change

* Make spotBugs happy
2021-08-15 22:18:40 +02:00
Sylvia van Os
86be5d1994 Update image-actions 2021-08-15 17:44:42 +02:00
Sylvia van Os
5be2308606 Release 2.2.3 2021-08-13 20:21:02 +02:00
Sylvia van Os
a2e81d08d7 Merge pull request #330 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-13 15:34:50 +02:00
Joel A
9f5f0e79f4 Translated using Weblate (Swedish)
Currently translated at 66.6% (2 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-08-13 13:32:16 +02:00
Joel A
3f8c4c94ce Translated using Weblate (Swedish)
Currently translated at 99.4% (171 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-13 13:32:16 +02:00
mondstern
46895c0037 Translated using Weblate (Finnish)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/fi/
2021-08-13 13:32:15 +02:00
mondstern
65c98a4840 Translated using Weblate (Finnish)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/fi/
2021-08-13 13:32:15 +02:00
Sylvia van Os
67c58c10b2 Create splash screen 2021-08-12 23:36:55 +02:00
Sylvia van Os
8d82a4616f Don't remove needs info 2021-08-12 13:06:15 +02:00
Sylvia van Os
01a272f8e6 Autoclose issues being in needs info state for over 90 days 2021-08-11 17:57:22 +02:00
Sylvia van Os
0235a397cf Merge pull request #328 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-11 09:04:18 +02:00
Joel A
e37ee6f812 Translated using Weblate (Swedish)
Currently translated at 85.4% (147 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-11 05:32:37 +02:00
mondstern
2b5ea47d9f Translated using Weblate (Bulgarian)
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/bg/
2021-08-11 05:32:37 +02:00
mondstern
0fcdaf3f60 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/bg/
2021-08-11 05:32:37 +02:00
Jane Kong
b2cb38b61d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (3 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/zh_Hans/
2021-08-11 05:32:36 +02:00
Jane Kong
fd1fe168cc Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/zh_Hans/
2021-08-11 05:32:36 +02:00
mondstern
9deb80ff9b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (172 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/uk/
2021-08-11 05:32:35 +02:00
Sylvia van Os
75fdd7390f Merge pull request #325 from recursiveribbons/sql-refactor
Refactored DBHelper
2021-08-09 20:35:55 +02:00
recursiveribbons
7cd6c17b55 Refactored DBHelper 2021-08-09 17:35:25 +02:00
Sylvia van Os
c273d8d17f Fix widget creating different-looking shortcut than app shortcuts 2021-08-08 23:49:10 +02:00
Sylvia van Os
c1b02f58ff Fix indent 2021-08-08 23:04:55 +02:00
Sylvia van Os
0bac2dfb62 Merge pull request #322 from TacoTheDank/master
Some cleanup and compatibility stuff
2021-08-08 23:03:58 +02:00
TacoTheDank
4f0288e396 Use ActivityCompat.recreate 2021-08-08 16:33:55 -04:00
TacoTheDank
55f0399325 Use ShortcutInfoCompat 2021-08-08 16:33:42 -04:00
Sylvia van Os
e756c6e50b Fix dir names 2021-08-08 19:30:37 +02:00
Sylvia van Os
b8c022dc70 Merge pull request #323 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-08 19:18:06 +02:00
Joel A
d823ddd8aa Translated using Weblate (Swedish)
Currently translated at 33.3% (1 of 3 strings)

Translation: Catima/Fastlane
Translate-URL: https://hosted.weblate.org/projects/catima/fastlane/sv/
2021-08-08 19:13:17 +02:00
Joel A
352b42bba0 Translated using Weblate (Swedish)
Currently translated at 82.5% (142 of 172 strings)

Translation: Catima/Catima
Translate-URL: https://hosted.weblate.org/projects/catima/catima/sv/
2021-08-08 19:13:17 +02:00
Sylvia van Os
89894131d9 Merge branch 'master' of github.com:TheLastProject/loyalty-card-locker 2021-08-08 19:12:49 +02:00
Sylvia van Os
5ddcb0a668 Fix crash on rotation in edit view 2021-08-08 19:12:25 +02:00
TacoTheDank
5d5b1f0a63 Use AndroidX ExifInterface 2021-08-07 17:08:50 -04:00
TacoTheDank
03e638786b Create TextWatcher stub 2021-08-07 17:08:28 -04:00
TacoTheDank
7d5b2f8dc8 Update NumberPickerPreference library 2021-08-07 16:47:01 -04:00
Sylvia van Os
13257d17fc Merge pull request #321 from weblate/weblate-catima-catima
Translations update from Weblate
2021-08-07 13:48:38 +02:00
Joel A
5e8a589d9a Added translation using Weblate (Swedish) 2021-08-07 13:47:51 +02:00
61 changed files with 952 additions and 486 deletions

View File

@@ -0,0 +1,23 @@
name: 'Close issues and PRs needing info for too long'
on:
schedule:
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-stale: -1
days-before-close: 90
close-issue-message: 'This issue is missing necessary information and cannot be worked on in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
close-pr-message: 'This PR is missing necessary information and cannot be merged in its current state. It has therefore been closed to keep the issue tracker clean. If you have more information, feel free to reopen it.'
only-labels: 'needs info'
stale-issue-label: 'needs info'
stale-pr-label: 'needs info'
remove-stale-when-updated: false

View File

@@ -10,16 +10,19 @@ on:
- '**.webp'
jobs:
build:
# Only run on Pull Requests within the same repository, and not from forks.
if: github.event.pull_request.head.repo.full_name == github.repository
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@master
uses: actions/checkout@v2
- name: Compress Images
id: calibre
uses: calibreapp/image-actions@master
uses: calibreapp/image-actions@1.1.0
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
ignorePaths: 'app/src/test'
compressOnly: true
- name: Create New Pull Request If Needed
if: steps.calibre.outputs.markdown != ''

View File

@@ -1,5 +1,25 @@
# Changelog
## v2.3.0 (2021-08-19)
Changes:
- Fix images not imported from backup
- Option to override language
## v2.2.3 (2021-08-13)
Changes:
- Fix widget creating different-looking shortcut than app shortcuts
- Replace default Android black screen with splash screen
## v2.2.2 (2021-08-08)
Changes:
- Fix crash on rotation in loyalty card edit activity
## v2.2.1 (2021-08-07)
Changes:

View File

@@ -18,8 +18,8 @@ android {
applicationId "me.hackerchick.catima"
minSdkVersion 19
targetSdkVersion 30
versionCode 77
versionName "2.2.1"
versionCode 80
versionName "2.3.0"
vectorDrawables.useSupportLibrary true
multiDexEnabled true
@@ -37,6 +37,12 @@ android {
}
}
bundle {
language {
enableSplit = false
}
}
compileOptions {
encoding "UTF-8"
@@ -77,6 +83,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.4.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
@@ -86,7 +93,7 @@ dependencies {
implementation 'com.google.zxing:core:3.4.1'
implementation 'org.apache.commons:commons-csv:1.8'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.2'
implementation 'com.github.invissvenska:NumberPickerPreference:1.0.3'
implementation 'net.lingala.zip4j:zip4j:2.8.0'
// SpotBugs

View File

@@ -29,7 +29,7 @@
<activity
android:name="protect.card_locker.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

View File

@@ -1,5 +1,6 @@
package protect.card_locker;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -21,10 +22,16 @@ public class AboutActivity extends AppCompatActivity
{
private static final String TAG = "Catima";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.about);
setContentView(R.layout.about_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

View File

@@ -1,11 +1,10 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.MenuItem;
@@ -65,11 +64,16 @@ public class BarcodeSelectorActivity extends AppCompatActivity
private Map<String, Pair<Integer, Integer>> barcodeViewMap;
private LinkedList<AsyncTask> barcodeGeneratorTasks = new LinkedList<>();
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.selectBarcodeTitle);
setContentView(R.layout.barcode_selector_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@@ -94,14 +98,8 @@ public class BarcodeSelectorActivity extends AppCompatActivity
barcodeViewMap.put(BarcodeFormat.UPC_E.name(), new Pair<>(R.id.upceBarcode, R.id.upceBarcodeText));
EditText cardId = findViewById(R.id.cardId);
cardId.addTextChangedListener(new TextWatcher()
cardId.addTextChangedListener(new SimpleTextWatcher()
{
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
// Noting to do
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
@@ -113,12 +111,6 @@ public class BarcodeSelectorActivity extends AppCompatActivity
setButtonListener(noBarcodeButtonView, s.toString());
noBarcodeButtonView.setEnabled(s.length() > 0);
}
@Override
public void afterTextChanged(Editable s)
{
// Noting to do
}
});
final Bundle b = getIntent().getExtras();

View File

@@ -1,8 +1,6 @@
package protect.card_locker;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -12,6 +10,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -69,23 +69,9 @@ public class CardShortcutConfigure extends AppCompatActivity implements LoyaltyC
Log.d(TAG, "Creating shortcut for card " + loyaltyCard.store + "," + loyaltyCard.id);
Intent shortcutIntent = new Intent(CardShortcutConfigure.this, LoyaltyCardViewActivity.class);
shortcutIntent.setAction(Intent.ACTION_MAIN);
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCard.id);
bundle.putBoolean("view", true);
shortcutIntent.putExtras(bundle);
ShortcutInfoCompat shortcut = ShortcutHelper.createShortcutBuilder(CardShortcutConfigure.this, loyaltyCard).build();
Bitmap iconBitmap = Utils.generateIcon(CardShortcutConfigure.this, loyaltyCard, true).getLetterTile();
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, loyaltyCard.store);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, iconBitmap);
setResult(RESULT_OK, intent);
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(CardShortcutConfigure.this, shortcut));
finish();
}

View File

@@ -3,6 +3,7 @@ package protect.card_locker;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
@@ -12,6 +13,7 @@ import com.google.zxing.BarcodeFormat;
import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Currency;
import java.util.Date;
import java.util.List;
@@ -270,8 +272,7 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS, starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return newId;
return db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
}
public long insertLoyaltyCard(final SQLiteDatabase db, final String store,
@@ -291,8 +292,7 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return newId;
return db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
}
public long insertLoyaltyCard(final SQLiteDatabase db, final int id, final String store,
@@ -313,8 +313,7 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
final long newId = db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
return newId;
return db.insert(LoyaltyCardDbIds.TABLE, null, contentValues);
}
public boolean updateLoyaltyCard(final int id, final String store, final String note,
@@ -335,8 +334,7 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbIds.BARCODE_TYPE, barcodeType != null ? barcodeType.toString() : null);
contentValues.put(LoyaltyCardDbIds.HEADER_COLOR, headerColor);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
whereAttrs(LoyaltyCardDbIds.ID), withArgs(id));
return (rowsUpdated == 1);
}
@@ -346,23 +344,21 @@ public class DBHelper extends SQLiteOpenHelper
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbIds.STAR_STATUS,starStatus);
int rowsUpdated = db.update(LoyaltyCardDbIds.TABLE, contentValues,
LoyaltyCardDbIds.ID + "=?",
new String[]{Integer.toString(id)});
whereAttrs(LoyaltyCardDbIds.ID),
withArgs(id));
return (rowsUpdated == 1);
}
public LoyaltyCard getLoyaltyCard(final int id)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
" where " + LoyaltyCardDbIds.ID + "=?", new String[]{String.format("%d", id)});
Cursor data = db.query(LoyaltyCardDbIds.TABLE, null, whereAttrs(LoyaltyCardDbIds.ID), withArgs(id), null, null, null);
LoyaltyCard card = null;
if(data.getCount() == 1)
{
data.moveToFirst();
card = LoyaltyCard.toLoyaltyCard(data);
}
@@ -377,7 +373,7 @@ public class DBHelper extends SQLiteOpenHelper
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE + " g " +
" LEFT JOIN " + LoyaltyCardDbIdsGroups.TABLE + " ig ON ig." + LoyaltyCardDbIdsGroups.groupID + " = g." + LoyaltyCardDbGroups.ID +
" where " + LoyaltyCardDbIdsGroups.cardID + "=?" +
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, new String[]{String.format("%d", id)});
" ORDER BY " + LoyaltyCardDbIdsGroups.groupID, withArgs(id));
List<Group> groups = new ArrayList<>();
@@ -403,8 +399,8 @@ public class DBHelper extends SQLiteOpenHelper
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
withArgs(id));
// Then create entries for selected values
for (Group group : groups) {
@@ -419,8 +415,8 @@ public class DBHelper extends SQLiteOpenHelper
{
// First delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
withArgs(id));
// Then create entries for selected values
for (Group group : groups) {
@@ -436,13 +432,13 @@ public class DBHelper extends SQLiteOpenHelper
SQLiteDatabase db = getWritableDatabase();
// Delete card
int rowsDeleted = db.delete(LoyaltyCardDbIds.TABLE,
LoyaltyCardDbIds.ID + " = ? ",
new String[]{String.format("%d", id)});
whereAttrs(LoyaltyCardDbIds.ID),
withArgs(id));
// And delete lookup table entries associated with this card
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.cardID + " = ? ",
new String[]{String.format("%d", id)});
whereAttrs(LoyaltyCardDbIdsGroups.cardID),
withArgs(id));
// Also wipe card images associated with this card
try {
@@ -492,11 +488,11 @@ public class DBHelper extends SQLiteOpenHelper
List<Integer> allowedIds = getGroupCardIds(group._id);
// Empty group
if (allowedIds.size() > 0) {
if (!allowedIds.isEmpty()) {
groupFilter.append("AND (");
for (int i = 0; i < allowedIds.size(); i++) {
groupFilter.append(LoyaltyCardDbIds.ID + " = " + allowedIds.get(i));
groupFilter.append(LoyaltyCardDbIds.ID + " = ").append(allowedIds.get(i));
if (i != allowedIds.size() - 1) {
groupFilter.append(" OR ");
}
@@ -507,20 +503,18 @@ public class DBHelper extends SQLiteOpenHelper
}
}
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
return db.rawQuery("select * from " + LoyaltyCardDbIds.TABLE +
" WHERE (" + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? )" +
groupFilter.toString() +
" ORDER BY " + LoyaltyCardDbIds.STAR_STATUS + " DESC," + LoyaltyCardDbIds.STORE + " COLLATE NOCASE ASC " +
limitString, selectionArgs, null);
return res;
}
public int getLoyaltyCardCount()
{
// An empty string will match everything
return getLoyaltyCardCount("");
SQLiteDatabase db = getReadableDatabase();
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIds.TABLE);
}
/**
@@ -532,25 +526,11 @@ public class DBHelper extends SQLiteOpenHelper
public int getLoyaltyCardCount(String filter)
{
String actualFilter = String.format("%%%s%%", filter);
String[] selectionArgs = { actualFilter, actualFilter };
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIds.TABLE +
" WHERE " + LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? "
, selectionArgs, null);
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIds.TABLE,
LoyaltyCardDbIds.STORE + " LIKE ? " +
" OR " + LoyaltyCardDbIds.NOTE + " LIKE ? ", withArgs(actualFilter, actualFilter));
}
/**
@@ -562,47 +542,40 @@ public class DBHelper extends SQLiteOpenHelper
{
SQLiteDatabase db = getReadableDatabase();
Cursor res = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
return db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" ORDER BY " + LoyaltyCardDbGroups.ORDER + " ASC," + LoyaltyCardDbGroups.ID + " COLLATE NOCASE ASC", null, null);
return res;
}
public List<Group> getGroups() {
Cursor data = getGroupCursor();
try(Cursor data = getGroupCursor()) {
List<Group> groups = new ArrayList<>();
List<Group> groups = new ArrayList<>();
if (!data.moveToFirst()) {
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
if (!data.moveToFirst()) {
data.close();
return groups;
}
groups.add(Group.toGroup(data));
while (data.moveToNext()) {
groups.add(Group.toGroup(data));
}
data.close();
return groups;
}
public void reorderGroups(final List<Group> groups)
{
Integer order = 0;
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues;
for (Group group : groups)
{
contentValues = new ContentValues();
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ORDER, order);
db.update(LoyaltyCardDbGroups.TABLE, contentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{group._id});
whereAttrs(LoyaltyCardDbGroups.ID),
withArgs(group._id));
order++;
}
@@ -611,18 +584,15 @@ public class DBHelper extends SQLiteOpenHelper
public Group getGroup(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("select * from " + LoyaltyCardDbGroups.TABLE +
" where " + LoyaltyCardDbGroups.ID + "=?", new String[]{groupName});
Cursor data = db.query(LoyaltyCardDbGroups.TABLE, null,
whereAttrs(LoyaltyCardDbGroups.ID), withArgs(groupName), null, null, null);
Group group = null;
if(data.getCount() == 1)
{
data.moveToFirst();
group = Group.toGroup(data);
}
data.close();
return group;
@@ -631,28 +601,14 @@ public class DBHelper extends SQLiteOpenHelper
public int getGroupCount()
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbGroups.TABLE, null);
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
}
data.close();
return numItems;
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbGroups.TABLE);
}
public List<Integer> getGroupCardIds(final String groupName)
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT " + LoyaltyCardDbIdsGroups.cardID +
" FROM " + LoyaltyCardDbIdsGroups.TABLE +
" WHERE " + LoyaltyCardDbIdsGroups.groupID + " =? ", new String[]{groupName});
Cursor data = db.query(LoyaltyCardDbIdsGroups.TABLE, withArgs(LoyaltyCardDbIdsGroups.cardID),
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName), null, null, null);
List<Integer> cardIds = new ArrayList<>();
if (!data.moveToFirst()) {
@@ -678,8 +634,7 @@ public class DBHelper extends SQLiteOpenHelper
ContentValues contentValues = new ContentValues();
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return newId;
return db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
}
public boolean insertGroup(final SQLiteDatabase db, final String name)
@@ -688,7 +643,7 @@ public class DBHelper extends SQLiteOpenHelper
contentValues.put(LoyaltyCardDbGroups.ID, name);
contentValues.put(LoyaltyCardDbGroups.ORDER, getGroupCount());
final long newId = db.insert(LoyaltyCardDbGroups.TABLE, null, contentValues);
return (newId != -1);
return newId != -1;
}
public boolean updateGroup(final String groupName, final String newName)
@@ -708,13 +663,13 @@ public class DBHelper extends SQLiteOpenHelper
try {
// Update group name
int groupsChanged = db.update(LoyaltyCardDbGroups.TABLE, groupContentValues,
LoyaltyCardDbGroups.ID + "=?",
new String[]{groupName});
whereAttrs(LoyaltyCardDbGroups.ID),
withArgs(groupName));
// Also update lookup tables
db.update(LoyaltyCardDbIdsGroups.TABLE, lookupContentValues,
LoyaltyCardDbIdsGroups.groupID + "=?",
new String[]{groupName});
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
withArgs(groupName));
if (groupsChanged == 1) {
db.setTransactionSuccessful();
@@ -738,13 +693,13 @@ public class DBHelper extends SQLiteOpenHelper
try {
// Delete group
int groupsDeleted = db.delete(LoyaltyCardDbGroups.TABLE,
LoyaltyCardDbGroups.ID + " = ? ",
new String[]{groupName});
whereAttrs(LoyaltyCardDbGroups.ID),
withArgs(groupName));
// And delete lookup table entries associated with this group
db.delete(LoyaltyCardDbIdsGroups.TABLE,
LoyaltyCardDbIdsGroups.groupID + " = ? ",
new String[]{groupName});
whereAttrs(LoyaltyCardDbIdsGroups.groupID),
withArgs(groupName));
if (groupsDeleted == 1) {
db.setTransactionSuccessful();
@@ -764,21 +719,24 @@ public class DBHelper extends SQLiteOpenHelper
{
SQLiteDatabase db = getReadableDatabase();
Cursor data = db.rawQuery("SELECT Count(*) FROM " + LoyaltyCardDbIdsGroups.TABLE +
" where " + LoyaltyCardDbIdsGroups.groupID + "=?",
new String[]{groupName});
return (int) DatabaseUtils.queryNumEntries(db, LoyaltyCardDbIdsGroups.TABLE,
whereAttrs(LoyaltyCardDbIdsGroups.groupID), withArgs(groupName));
}
int numItems = 0;
if(data.getCount() == 1)
{
data.moveToFirst();
numItems = data.getInt(0);
private String whereAttrs(String... attrs) {
if (attrs.length == 0) {
return null;
}
StringBuilder whereClause = new StringBuilder(attrs[0]).append("=?");
for (int i = 1; i < attrs.length; i++) {
whereClause.append(" AND ").append(attrs[i]).append("=?");
}
return whereClause.toString();
}
data.close();
return numItems;
private String[] withArgs(Object... object) {
return Arrays.stream(object)
.map(String::valueOf)
.toArray(String[]::new);
}
}

View File

@@ -2,6 +2,7 @@ package protect.card_locker;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -48,10 +49,16 @@ public class ImportExportActivity extends AppCompatActivity
private String importAlertMessage;
private DataFormat importDataFormat;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.importExport);
setContentView(R.layout.import_export_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

View File

@@ -1,6 +1,8 @@
package protect.card_locker;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.zxing.BarcodeFormat;
@@ -10,7 +12,7 @@ import java.util.Date;
import androidx.annotation.Nullable;
public class LoyaltyCard {
public class LoyaltyCard implements Parcelable {
public final int id;
public final String store;
public final String note;
@@ -22,6 +24,7 @@ public class LoyaltyCard {
@Nullable
public final String barcodeId;
@Nullable
public final BarcodeFormat barcodeType;
@Nullable
@@ -31,7 +34,7 @@ public class LoyaltyCard {
public LoyaltyCard(final int id, final String store, final String note, final Date expiry,
final BigDecimal balance, final Currency balanceType, final String cardId,
final String barcodeId, final BarcodeFormat barcodeType, final Integer headerColor,
@Nullable final String barcodeId, @Nullable final BarcodeFormat barcodeType, @Nullable final Integer headerColor,
final int starStatus)
{
this.id = id;
@@ -47,6 +50,38 @@ public class LoyaltyCard {
this.starStatus = starStatus;
}
protected LoyaltyCard(Parcel in) {
id = in.readInt();
store = in.readString();
note = in.readString();
long tmpExpiry = in.readLong();
expiry = tmpExpiry != -1 ? new Date(tmpExpiry) : null;
balance = (BigDecimal) in.readValue(BigDecimal.class.getClassLoader());
balanceType = (Currency) in.readValue(Currency.class.getClassLoader());
cardId = in.readString();
barcodeId = in.readString();
String tmpBarcodeType = in.readString();
barcodeType = !tmpBarcodeType.isEmpty() ? BarcodeFormat.valueOf(tmpBarcodeType) : null;
int tmpHeaderColor = in.readInt();
headerColor = tmpHeaderColor != -1 ? tmpHeaderColor : null;
starStatus = in.readInt();
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(store);
parcel.writeString(note);
parcel.writeLong(expiry != null ? expiry.getTime() : -1);
parcel.writeValue(balance);
parcel.writeValue(balanceType);
parcel.writeString(cardId);
parcel.writeString(barcodeId);
parcel.writeString(barcodeType != null ? barcodeType.toString() : "");
parcel.writeInt(headerColor != null ? headerColor : -1);
parcel.writeInt(starStatus);
}
public static LoyaltyCard toLoyaltyCard(Cursor cursor)
{
int id = cursor.getInt(cursor.getColumnIndexOrThrow(DBHelper.LoyaltyCardDbIds.ID));
@@ -89,4 +124,21 @@ public class LoyaltyCard {
return new LoyaltyCard(id, store, note, expiry, balance, balanceType, cardId, barcodeId, barcodeType, headerColor, starred);
}
@Override
public int describeContents() {
return id;
}
public static final Creator<LoyaltyCard> CREATOR = new Creator<LoyaltyCard>() {
@Override
public LoyaltyCard createFromParcel(Parcel in) {
return new LoyaltyCard(in);
}
@Override
public LoyaltyCard[] newArray(int size) {
return new LoyaltyCard[size];
}
};
}

View File

@@ -12,7 +12,6 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -21,7 +20,6 @@ import android.os.LocaleList;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -71,6 +69,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface;
import androidx.fragment.app.DialogFragment;
public class LoyaltyCardEditActivity extends AppCompatActivity
@@ -78,6 +77,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
private static final String TAG = "Catima";
private final String STATE_TAB_INDEX = "savedTab";
private final String STATE_TEMP_CARD = "tempLoyaltyCard";
private static final int ID_IMAGE_FRONT = 0;
private static final int ID_IMAGE_BACK = 1;
@@ -141,6 +141,11 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
LoyaltyCard tempLoyaltyCard;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
private static LoyaltyCard updateTempState(LoyaltyCard loyaltyCard, LoyaltyCardField fieldName, Object value) {
return new LoyaltyCard(
(int) (fieldName == LoyaltyCardField.id ? value : loyaltyCard.id),
@@ -185,10 +190,12 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
super.onSaveInstanceState(savedInstanceState);
tabs = findViewById(R.id.tabs);
savedInstanceState.putInt(STATE_TAB_INDEX, tabs.getSelectedTabPosition());
savedInstanceState.putParcelable(STATE_TEMP_CARD, tempLoyaltyCard);
}
@Override
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
tempLoyaltyCard = savedInstanceState.getParcelable(STATE_TEMP_CARD);
super.onRestoreInstanceState(savedInstanceState);
tabs = findViewById(R.id.tabs);
tabs.selectTab(tabs.getTabAt(savedInstanceState.getInt(STATE_TAB_INDEX)));
@@ -245,34 +252,22 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
};
storeFieldEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
storeFieldEdit.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
updateTempState(LoyaltyCardField.store, s.toString());
generateIcon(s.toString());
}
@Override
public void afterTextChanged(Editable s) { }
});
noteFieldEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
noteFieldEdit.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
updateTempState(LoyaltyCardField.note, s.toString());
}
@Override
public void afterTextChanged(Editable s) { }
});
expiryField.addTextChangedListener(new TextWatcher() {
expiryField.addTextChangedListener(new SimpleTextWatcher() {
CharSequence lastValue;
@Override
@@ -311,10 +306,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
});
balanceField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
balanceField.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
@@ -327,15 +319,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
e.printStackTrace();
}
}
@Override
public void afterTextChanged(Editable s) { }
});
balanceCurrencyField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
balanceCurrencyField.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Currency currency;
@@ -391,7 +377,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
});
cardIdFieldView.addTextChangedListener(new TextWatcher() {
cardIdFieldView.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (initDone && !onResuming) {
@@ -411,12 +397,9 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
public void onTextChanged(CharSequence s, int start, int before, int count) {
updateTempState(LoyaltyCardField.cardId, s.toString());
}
@Override
public void afterTextChanged(Editable s) { }
});
barcodeIdField.addTextChangedListener(new TextWatcher() {
barcodeIdField.addTextChangedListener(new SimpleTextWatcher() {
CharSequence lastValue;
@Override
@@ -475,10 +458,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity
}
});
barcodeTypeField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
barcodeTypeField.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!s.toString().isEmpty()) {

View File

@@ -1,5 +1,6 @@
package protect.card_locker;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -104,6 +105,11 @@ public class LoyaltyCardViewActivity extends AppCompatActivity
IMAGE_BACK
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
private void extractIntentFields(Intent intent)
{
final Bundle b = intent.getExtras();

View File

@@ -29,6 +29,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -49,6 +50,11 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
private View mHelpText;
private View mNoMatchingCardsText;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback()
{
@Override
@@ -176,7 +182,9 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
@Override
protected void onCreate(Bundle inputSavedInstanceState)
{
setTheme(R.style.AppTheme_NoActionBar);
super.onCreate(inputSavedInstanceState);
setTitle(R.string.app_name);
setContentView(R.layout.main_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
@@ -340,7 +348,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
MenuItem searchItem = mMenu.findItem(R.id.action_search);
searchItem.collapseActionView();
}
recreate();
ActivityCompat.recreate(this);
return;
}
@@ -695,7 +703,7 @@ public class MainActivity extends AppCompatActivity implements LoyaltyCardCursor
b.putInt("id", loyaltyCard.id);
i.putExtras(b);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i);
ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);
startActivityForResult(i, Utils.MAIN_REQUEST);
}

View File

@@ -2,7 +2,6 @@ package protect.card_locker;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.text.InputType;
import android.view.MenuItem;
@@ -32,10 +31,16 @@ public class ManageGroupsActivity extends AppCompatActivity implements GroupCurs
private RecyclerView mGroupList;
GroupCursorAdapter mAdapter;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.groups);
setContentView(R.layout.manage_groups_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

View File

@@ -1,6 +1,7 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -39,6 +40,11 @@ public class ScanActivity extends AppCompatActivity {
private String addGroup;
private boolean torch = false;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
private void extractIntentFields(Intent intent) {
final Bundle b = intent.getExtras();
cardId = b != null ? b.getString(LoyaltyCardEditActivity.BUNDLE_CARDID) : null;
@@ -49,6 +55,7 @@ public class ScanActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.scanCardBarcode);
setContentView(R.layout.scan_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

View File

@@ -1,19 +1,19 @@
package protect.card_locker;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
class ShortcutHelper
{
// Android documentation says that no more than 5 shortcuts
@@ -30,135 +30,113 @@ class ShortcutHelper
* card exceeds the max number of shortcuts, then the least recently
* used card shortcut is discarded.
*/
@TargetApi(25)
static void updateShortcuts(Context context, LoyaltyCard card, Intent intent)
static void updateShortcuts(Context context, LoyaltyCard card)
{
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
LinkedList<ShortcutInfoCompat> list = new LinkedList<>(ShortcutManagerCompat.getDynamicShortcuts(context));
DBHelper dbHelper = new DBHelper(context);
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++)
{
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
LinkedList<ShortcutInfo> list = new LinkedList<>(shortcutManager.getDynamicShortcuts());
DBHelper dbHelper = new DBHelper(context);
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, new Comparator<ShortcutInfo>()
if(list.get(index).getId().equals(shortcutId))
{
@Override
public int compare(ShortcutInfo o1, ShortcutInfo o2)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
{
return o1.getRank() - o2.getRank();
}
else
{
return 0;
}
}
});
// Found the item already
foundIndex = index;
break;
}
}
Integer foundIndex = null;
for(int index = 0; index < list.size(); index++)
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. First, we need to trim the list
// until it is able to accept a new item, then the item is
// inserted.
while(list.size() >= MAX_SHORTCUTS)
{
if(list.get(index).getId().equals(shortcutId))
{
// Found the item already
foundIndex = index;
break;
}
list.pollLast();
}
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.
ShortcutInfo found = list.remove(foundIndex.intValue());
list.addFirst(found);
}
else
{
// The item is new to the list. First, we need to trim the list
// until it is able to accept a new item, then the item is
// inserted.
while(list.size() >= MAX_SHORTCUTS)
{
list.pollLast();
}
ShortcutInfoCompat shortcut = createShortcutBuilder(context, card).build();
ShortcutInfo shortcut = new ShortcutInfo.Builder(context, Integer.toString(card.id))
.setShortLabel(card.store)
.setLongLabel(card.store)
.setIntent(intent)
.build();
list.addFirst(shortcut);
}
list.addFirst(shortcut);
}
LinkedList<ShortcutInfoCompat> finalList = new LinkedList<>();
LinkedList<ShortcutInfo> finalList = new LinkedList<>();
// 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);
// The ranks are now updated; the order in the list is the rank.
for(int index = 0; index < list.size(); index++)
{
ShortcutInfo prevShortcut = list.get(index);
LoyaltyCard loyaltyCard = dbHelper.getLoyaltyCard(Integer.parseInt(prevShortcut.getId()));
Intent shortcutIntent = prevShortcut.getIntent();
Bitmap iconBitmap = Utils.generateIcon(context, dbHelper.getLoyaltyCard(Integer.parseInt(prevShortcut.getId())), true).getLetterTile();
Icon icon;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
icon = Icon.createWithAdaptiveBitmap(iconBitmap);
} else {
icon = Icon.createWithBitmap(iconBitmap);
}
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
shortcutIntent.setFlags(shortcutIntent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
ShortcutInfo updatedShortcut = new ShortcutInfo.Builder(context, prevShortcut.getId())
.setShortLabel(prevShortcut.getShortLabel())
.setLongLabel(prevShortcut.getLongLabel())
.setIntent(shortcutIntent)
.setIcon(icon)
ShortcutInfoCompat updatedShortcut = createShortcutBuilder(context, loyaltyCard)
.setRank(index)
.build();
finalList.addLast(updatedShortcut);
}
shortcutManager.setDynamicShortcuts(finalList);
finalList.addLast(updatedShortcut);
}
ShortcutManagerCompat.setDynamicShortcuts(context, finalList);
}
/**
* Remove the given card id from the app shortcuts, if such a
* shortcut exists.
*/
@TargetApi(25)
static void removeShortcut(Context context, int cardId)
{
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1)
List<ShortcutInfoCompat> list = ShortcutManagerCompat.getDynamicShortcuts(context);
String shortcutId = Integer.toString(cardId);
for(int index = 0; index < list.size(); index++)
{
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
List<ShortcutInfo> list = shortcutManager.getDynamicShortcuts();
String shortcutId = Integer.toString(cardId);
for(int index = 0; index < list.size(); index++)
if(list.get(index).getId().equals(shortcutId))
{
if(list.get(index).getId().equals(shortcutId))
{
list.remove(index);
break;
}
list.remove(index);
break;
}
shortcutManager.setDynamicShortcuts(list);
}
ShortcutManagerCompat.setDynamicShortcuts(context, list);
}
static ShortcutInfoCompat.Builder createShortcutBuilder(Context context, LoyaltyCard loyaltyCard) {
Intent intent = new Intent(context, LoyaltyCardViewActivity.class);
intent.setAction(Intent.ACTION_MAIN);
// Prevent instances of the view activity from piling up; if one exists let this
// one replace it.
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_SINGLE_TOP);
final Bundle bundle = new Bundle();
bundle.putInt("id", loyaltyCard.id);
bundle.putBoolean("view", true);
intent.putExtras(bundle);
Bitmap iconBitmap = Utils.generateIcon(context, loyaltyCard, true).getLetterTile();
IconCompat icon = IconCompat.createWithAdaptiveBitmap(iconBitmap);
return new ShortcutInfoCompat.Builder(context, Integer.toString(loyaltyCard.id))
.setShortLabel(loyaltyCard.store)
.setLongLabel(loyaltyCard.store)
.setIntent(intent)
.setIcon(icon);
}
}

View File

@@ -0,0 +1,15 @@
package protect.card_locker;
import android.text.Editable;
import android.text.TextWatcher;
public class SimpleTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
@Override
public void afterTextChanged(Editable s) { }
}

View File

@@ -3,11 +3,14 @@ package protect.card_locker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Build;
import android.os.LocaleList;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;
@@ -32,8 +35,11 @@ import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;
import androidx.core.graphics.ColorUtils;
import protect.card_locker.preferences.Settings;
import androidx.exifinterface.media.ExifInterface;
public class Utils {
private static final String TAG = "Catima";
@@ -343,4 +349,35 @@ public class Utils {
static public Object hashmapGetOrDefault(HashMap hashMap, String key, Object defaultValue) {
return hashmapGetOrDefault(hashMap, key, defaultValue, String.class);
}
static public Locale stringToLocale(String localeString) {
String[] localeParts = localeString.split("-");
if (localeParts.length == 1) {
return new Locale(localeParts[0]);
}
if (localeParts[1].startsWith("r")) {
localeParts[1] = localeParts[1].substring(1);
}
return new Locale(localeParts[0], localeParts[1]);
}
static public Context updateBaseContextLocale(Context context) {
Settings settings = new Settings(context);
Locale chosenLocale = settings.getLocale();
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
configuration.locale = chosenLocale != null ? chosenLocale : Locale.getDefault();
res.updateConfiguration(configuration, res.getDisplayMetrics());
return context;
}
LocaleList localeList = chosenLocale != null ? new LocaleList(chosenLocale) : LocaleList.getDefault();
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
return context.createConfigurationContext(configuration);
}
}

View File

@@ -2,6 +2,7 @@ package protect.card_locker.importexport;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import com.google.zxing.BarcodeFormat;
@@ -47,24 +48,27 @@ public class CatimaImporter implements Importer
// First, check if this is a zip file
ZipInputStream zipInputStream = new ZipInputStream(bufferedInputStream);
LocalFileHeader localFileHeader = zipInputStream.getNextEntry();
if (localFileHeader == null) {
boolean isZipFile = false;
LocalFileHeader localFileHeader;
while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
isZipFile = true;
String fileName = Uri.parse(localFileHeader.getFileName()).getLastPathSegment();
if (fileName.equals("catima.csv")) {
importCSV(context, db, new ByteArrayInputStream(ZipUtils.read(zipInputStream).getBytes(StandardCharsets.UTF_8)));
} else if (fileName.endsWith(".png")) {
Utils.saveCardImage(context, ZipUtils.readImage(zipInputStream), fileName);
} else {
throw new FormatException("Unexpected file in import: " + fileName);
}
}
if (!isZipFile) {
// This is not a zip file, try importing as bare CSV
bufferedInputStream.reset();
importCSV(context, db, bufferedInputStream);
return;
}
importZipFile(context, db, zipInputStream, localFileHeader);
}
public void importZipFile(Context context, DBHelper db, ZipInputStream input, LocalFileHeader localFileHeader) throws IOException, FormatException, InterruptedException {
String fileName = localFileHeader.getFileName();
if (fileName.equals("catima.csv")) {
importCSV(context, db, new ByteArrayInputStream(ZipUtils.read(input).getBytes(StandardCharsets.UTF_8)));
} else {
Utils.saveCardImage(context, ZipUtils.readImage(input), fileName);
}
}

View File

@@ -3,11 +3,14 @@ package protect.card_locker.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Locale;
import androidx.annotation.IntegerRes;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import protect.card_locker.R;
import protect.card_locker.Utils;
public class Settings
{
@@ -45,6 +48,17 @@ public class Settings
return settings.getBoolean(getResString(keyId), defaultValue);
}
public Locale getLocale()
{
String value = getString(R.string.settings_key_locale, "");
if (value.length() == 0) {
return null;
}
return Utils.stringToLocale(value);
}
public int getTheme()
{
String value = getString(R.string.settings_key_theme, getResString(R.string.settings_key_system_theme));

View File

@@ -1,25 +1,47 @@
package protect.card_locker.preferences;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import com.journeyapps.barcodescanner.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import nl.invissvenska.numberpickerpreference.NumberDialogPreference;
import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment;
import protect.card_locker.MainActivity;
import protect.card_locker.R;
import protect.card_locker.Utils;
public class SettingsActivity extends AppCompatActivity
{
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(Utils.updateBaseContextLocale(base));
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setTitle(R.string.settings);
setContentView(R.layout.settings_activity);
ActionBar actionBar = getSupportActionBar();
@@ -29,8 +51,10 @@ public class SettingsActivity extends AppCompatActivity
}
// Display the fragment as the main content.
SettingsFragment fragment = new SettingsFragment();
fragment.setParentReference(this);
getSupportFragmentManager().beginTransaction()
.replace(R.id.settings_container, new SettingsFragment())
.replace(R.id.settings_container, fragment)
.commit();
}
@@ -51,46 +75,63 @@ public class SettingsActivity extends AppCompatActivity
public static class SettingsFragment extends PreferenceFragmentCompat
{
private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment";
private SettingsActivity parent;
public void setParentReference(SettingsActivity settingsActivity) {
parent = settingsActivity;
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
// Show pretty names
ListPreference localePreference = findPreference(getResources().getString(R.string.settings_key_locale));
assert localePreference != null;
CharSequence[] entryValues = localePreference.getEntryValues();
List<CharSequence> entries = new ArrayList<>();
for (CharSequence entry : entryValues) {
if (entry.length() == 0) {
entries.add(getResources().getString(R.string.settings_system_locale));
} else {
Locale entryLocale = Utils.stringToLocale(entry.toString());
entries.add(entryLocale.getDisplayName(entryLocale));
}
}
localePreference.setEntries(entries.toArray(new CharSequence[entryValues.length]));
Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme));
assert themePreference != null;
themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
{
@Override
public boolean onPreferenceChange(Preference preference, Object o)
{
if(o.toString().equals(getResources().getString(R.string.settings_key_light_theme)))
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
else if(o.toString().equals(getResources().getString(R.string.settings_key_dark_theme)))
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
else
{
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
FragmentActivity activity = getActivity();
if (activity != null) {
activity.recreate();
}
return true;
themePreference.setOnPreferenceChangeListener((preference, o) -> {
if (o.toString().equals(getResources().getString(R.string.settings_key_light_theme))) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else if (o.toString().equals(getResources().getString(R.string.settings_key_dark_theme))) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
FragmentActivity activity = getActivity();
if (activity != null) {
ActivityCompat.recreate(activity);
}
return true;
});
localePreference.setOnPreferenceChangeListener((preference, newValue) -> {
// Refresh the activity
parent.finish();
startActivity(parent.getIntent());
return true;
});
}
@Override
public void onDisplayPreferenceDialog(Preference preference)
{
if (preference instanceof NumberDialogPreference)
{
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof NumberDialogPreference) {
NumberDialogPreference dialogPreference = (NumberDialogPreference) preference;
DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment
.newInstance(
@@ -102,9 +143,7 @@ public class SettingsActivity extends AppCompatActivity
);
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
}
else
{
} else {
super.onDisplayPreferenceDialog(preference);
}
}

View File

@@ -0,0 +1,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/ic_launcher_background"/>
<item android:gravity="center"
android:width="256dp"
android:height="256dp"
android:drawable="@drawable/ic_launcher_foreground" />
</layer-list>

View File

@@ -158,21 +158,21 @@
<string name="importVoucherVault">Внасяне от Voucher Vault</string>
<string name="importVoucherVaultMessage">Изберете файла <i>vouchervault.json</i>, предварително изнесен от Voucher Vault.
\nИли създайте такъв файл от меню Изнасяне от Voucher Vault.</string>
<string name="importStocardMessage">Изберете файла <i>***-sync.zip</i>, предварително изнесен от Stocard и ръчно изберете вида на щрихкодовете.
\nИли получете такъв файл като пишете до support@stocardapp.com с молба за копие от вашата информация.</string>
<string name="importStocardMessage">Изберете вашия <i>***-sync.zip</i> експорт от Stocard, за да го импортирате.
\nИли го получете, като изпратите имейл на support@stocardapp.com с искане за експорт на вашите данни.</string>
<string name="importLoyaltyCardKeychainMessage">Изберете файла <i>LoyaltyCardKeychain.csv</i>, предварително изнесен от Loyalty Card Keychain.
\nИли създайте такъв файл от меню Внасяне/изнасяне от друго устройство със Loyalty Card Keychain като изберете Изнасяне.</string>
<string name="failedParsingImportUriError">Препратката не може да бъде анализирана за внасяне</string>
<string name="card_ids_copied">[не превеждай този низ, https://github.com/TheLastProject/Catima/issues/278]</string>
<string name="failedGeneratingShareURL">Грешка при създаване на адрес за споделяне. Изпратете доклад за дефект.</string>
<string name="deleteTitle">Премахване на карта</string>
<string name="deleteTitle">Изтрийте картата</string>
<plurals name="deleteCardsTitle">
<item quantity="one">Премахване на карта</item>
<item quantity="other">Премахване на карти</item>
<item quantity="one">Изтриване на <xliff:g>%d</xliff:g> карта</item>
<item quantity="other">Изтриване на <xliff:g>%d</xliff:g> карти</item>
</plurals>
<string name="deleteConfirmation">Потвърдете премахване на карта</string>
<string name="deleteConfirmation">Потвърдете премахване на карта\?</string>
<plurals name="deleteCardsConfirmation">
<item quantity="one">Желаете ли картата да бъде премахната\?</item>
<item quantity="other">Желаете ли <xliff:g>%d</xliff:g> картати да бъдат премахнати\?</item>
<item quantity="one">Изтриване на тази <xliff:g>%d</xliff:g> карта завинаги\?</item>
<item quantity="other">Изтриване на тези <xliff:g>%d</xliff:g> карти за постоянно\?</item>
</plurals>
</resources>

View File

@@ -72,4 +72,28 @@
<string name="barcodeType">Typ čárového kódu</string>
<string name="noMatchingGiftCards">Nic jsem nenašel. Zkuste změnit vyhledávání.</string>
<string name="action_search">Vyhledávání</string>
<string name="thumbnailDescription">Miniatura karty</string>
<string name="card_ids_copied">Zkopírované ID karty(karet)</string>
<plurals name="deleteCardsConfirmation">
<item quantity="one">Opravdu chcete kartu \u0020<xliff:g>%d</xliff:g> trvale odstranit\?</item>
<item quantity="few">Opravdu chcete karty \u0020<xliff:g>%d</xliff:g> \u0020trvale odstranit\?</item>
<item quantity="other"></item>
</plurals>
<plurals name="deleteCardsTitle">
<item quantity="one">Odstranit kartu \u0020<xliff:g>%d</xliff:g></item>
<item quantity="few">Odstranit karty <xliff:g>%d</xliff:g></item>
<item quantity="other"></item>
</plurals>
<string name="importSuccessful">Data karty 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_lock_barcode_orientation">Zamknout orientaci čárového kódu</string>
<string name="settings_max_font_size_scale">Maximální velikost písma</string>
<string name="settings_dark_theme">Tmavý</string>
<string name="settings_light_theme">Světlý</string>
<string name="settings_system_theme">Systém</string>
<string name="settings_theme">Vzhled</string>
<string name="settings_category_title_ui">Uživatelské rozhraní</string>
<string name="settings">Nastavení</string>
</resources>

View File

@@ -175,4 +175,6 @@
<item quantity="one"><xliff:g>%d</xliff:g> Karte löschen</item>
<item quantity="other"><xliff:g>%d</xliff:g> Karten löschen</item>
</plurals>
<string name="settings_system_locale">System</string>
<string name="settings_locale">Sprache</string>
</resources>

View File

@@ -6,30 +6,30 @@
<string name="updateBarcodeQuestionText">Vaihdoit kortin ID-tunnuksen. Haluatko päivittää myös viivakoodin käyttämään samaa arvoa\?</string>
<string name="updateBarcodeQuestionTitle">Päivitä viivakoodin arvo\?</string>
<string name="intent_import_card_from_url_share_multiple_text">Haluan jakaa joitain kortteja kanssasi</string>
<string name="copy_to_clipboard_multiple_toast">Kopioitiin korttitunnukset leikepöydälle</string>
<string name="copy_to_clipboard_multiple_toast">Kortin tunnukset kopioidaan leikepöydälle</string>
<string name="wrongValueForBarcodeType">Arvo ei ole kelvollinen valitulle viivakoodityypille</string>
<string name="unsupportedBarcodeType">Tätä viivakoodityyppiä ei voi vielä näyttää. Sitä saatetaan tukea sovelluksen uudemmassa versiossa.</string>
<string name="unsupportedBarcodeType">Tätä viivakoodityyppiä ei voi vielä näyttää. Sitä saatetaan tukea sovelluksen myöhemmässä versiossa.</string>
<string name="setBarcodeId">Aseta viivakoodin arvo</string>
<string name="sameAsCardId">Sama kuin kortin ID-tunnus</string>
<string name="barcodeId">Viivakoodin arvo</string>
<string name="importVoucherVaultMessage">Etsi tiedostoa joka on todennäköisesti nimetty nimellä <i>vouchervault.json</i> tuotavaksi.
\nTai luo se Vie toiminnolla Voucher Vault sovelluksessa.</string>
<string name="importVoucherVaultMessage">Valitse tuotava <i>vouchervault.json</i>-vienti Voucher Vaultista.
\nTai luo se painamalla ensin Vie Voucher Vaultissa.</string>
<string name="importVoucherVault">Tuo Voucher Vault varmuuskopiotiedostosta</string>
<string name="importLoyaltyCardKeychainMessage">Etsi tiedostoa joka on todennäköisesti nimetty nimellä <i>LoyaltyCardKeychain.csv</i> tuotavaksi.
\nTai luo se Tuo/Vie toiminnolla Loyalty Card Keychain sovelluksessa, valitsemalla valikosta Vie.</string>
<string name="importLoyaltyCardKeychainMessage">Valitse <i>LoyaltyCardKeychain.csv</i>-vientitietosi Loyalty Card Keychainista tuotavaksi.
\nTai luo se Loyalty Card Keychainin Import/Export-valikosta painamalla ensin Export (Vie).</string>
<string name="importLoyaltyCardKeychain">Tuo Loyalty Card Keychain varmuuskopiotiedostosta</string>
<string name="importFidmeMessage">Etsi tiedostoa joka on todennäköisesti nimetty nimellä <i>fidme-export-request-xxxxxx.zip</i> tuotavaksi ja valitse viivakoodityypit manuaalisesti jälkeenpäin.
\nTai luo se Tuo/Vie toiminnolla FidMe profiilistasi, valitsemalla Tietotosuoja ja sitten valitsemalla Vie tietoni.</string>
<string name="importFidmeMessage">Valitse <i>fidme-export-request-xxxxxxxx.zip</i>-vienti FidMe:stä tuotavaksi ja valitse viivakoodityypit manuaalisesti sen jälkeen.
\nTai luo se FidMe-profiilistasi valitsemalla Tietosuoja ja painamalla ensin Extract my data.</string>
<string name="importFidme">Tuo FidMe varmuuskopiotiedostosta</string>
<string name="importCatimaMessage">Etsi tiedostoa joka on todennäköisesti nimetty nimellä <i>Catima.csv</i> tuotavaksi.
\nTai luo se Tuo/Vie toiminnolla Catima sovelluksessa, valitsemalla valikosta Vie.</string>
<string name="importCatimaMessage">Valitse tuotava <i>catima.zip</i>-vienti Catimasta.
\nTai luo se jonkin toisen Catima-sovelluksen Tuo/Vie-valikosta painamalla siellä ensin Vie.</string>
<string name="importCatima">Tuo Catima varmuuskopiotiedostosta</string>
<string name="accept">Hyväksy</string>
<string name="privacy_policy_popup_text">Tietosuojaseloste (joidenkin sovelluskauppojen vaatimus):
\n
\nMITÄÄN TIETOJA EI KERÄTÄ LAINKAAN, minkä kuka tahansa voi vahvistaa, koska sovelluksemma on vapaa ohjelmisto.</string>
<string name="privacy_policy">Tietosuojakäytäntö</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="app_loyalty_card_keychain">Kanta-asiakaskortin avainnippu</string>
<string name="chooseImportType">Tuo tietoja kohteesta\?</string>
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> ei vaikuta olevan kelvollinen saldo.</string>
<string name="points">Pisteet</string>
@@ -85,8 +85,8 @@
<string name="copy_to_clipboard_toast">Kortin ID-tunnus kopioitu leikepöydälle</string>
<string name="enterBarcodeInstructions">Syötä kortin ID-tunnus ja valitse sen viivakoodityyppi, tai valitse \"Tällä kortilla ei ole viivakoodia\".</string>
<string name="selectBarcodeTitle">Valitse viivakoodi</string>
<string name="app_resources">Kolmannen osapuolen vapaat resurssit: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="app_libraries">Kolmannen osapuolen vapaat kirjastot: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_resources">Vapaat kolmannen osapuolen resurssit: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="app_libraries">Kolmannen osapuolen Libre-kirjastot: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="app_revision_fmt">Muutostiedot: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="debug_version_fmt">Versio: <xliff:g id="version">%s</xliff:g></string>
<string name="about_title_fmt">Tietoja <xliff:g id="app_name">%s</xliff:g></string>
@@ -156,4 +156,23 @@
<string name="frontImageDescription">Kortin etukuva</string>
<string name="deleteConfirmation">Poista tämä kortti\?</string>
<string name="deleteTitle">Poista kortti</string>
<plurals name="deleteCardsConfirmation">
<item quantity="one">Poista tämä <xliff:g>%d</xliff:g> kortti pysyvästi\?</item>
<item quantity="other">Poista tämä <xliff:g>%d</xliff:g> kortit pysyvästi\?</item>
</plurals>
<plurals name="deleteCardsTitle">
<item quantity="one">Poista <xliff:g>%d</xliff:g>kortti</item>
<item quantity="other">Poista <xliff:g>%d</xliff:g>kortit</item>
</plurals>
<plurals name="selectedCardCount">
<item quantity="one"><xliff:g>%d</xliff:g> Kortti valittu</item>
<item quantity="other"><xliff:g>%d</xliff:g> kortit valitut</item>
</plurals>
<string name="importStocard">Tuo Stocardista</string>
<string name="importStocardMessage">Valitse tuotava <i>***-sync.zip</i>-vienti Stocardista.
\nTai hanki se lähettämällä sähköpostia osoitteeseen support@stocardapp.com ja pyytämällä tietojesi vientiä.</string>
<string name="passwordRequired">Ole hyvä ja syötä salasana</string>
<string name="failedGeneratingShareURL">Ei pystynyt luomaan jaettavaa URL-osoitetta. Ilmoita tästä.</string>
<string name="turn_flashlight_on">Sytytä taskulamppu</string>
<string name="turn_flashlight_off">Sammuta salamavalo</string>
</resources>

View File

@@ -175,4 +175,6 @@
<item quantity="one">Supprimer <xliff:g>%d</xliff:g> carte</item>
<item quantity="other">Supprimer <xliff:g>%d</xliff:g> cartes</item>
</plurals>
<string name="settings_system_locale">Système</string>
<string name="settings_locale">Langue</string>
</resources>

View File

@@ -175,4 +175,6 @@
<item quantity="one">Elimina <xliff:g>%d</xliff:g> carta</item>
<item quantity="other">Elimina <xliff:g>%d</xliff:g> carte</item>
</plurals>
<string name="settings_system_locale">Sistema</string>
<string name="settings_locale">Lingua</string>
</resources>

View File

@@ -141,10 +141,10 @@
<string name="action_search">検索</string>
<string name="intent_import_card_from_url_share_multiple_text">カードを共有しましょう</string>
<string name="copy_to_clipboard_multiple_toast">カード番号をクリップボードにコピーしました</string>
<string name="card_ids_copied">コピーしたカード</string>
<string name="card_ids_copied">カード番号をコピーしました</string>
<string name="turn_flashlight_off">ライトをオフにする</string>
<string name="turn_flashlight_on">ライトをオンにする</string>
<string name="failedGeneratingShareURL">共有URLの生成に失敗しました。バグを報告してください。</string>
<string name="failedGeneratingShareURL">共有URLの生成を生成できませんでした。バグを報告してください。</string>
<string name="passwordRequired">パスワードを入力してください</string>
<string name="no">いいえ</string>
<string name="yes">はい</string>
@@ -157,7 +157,7 @@
<string name="photos">フォト</string>
<string name="backImageDescription">裏面</string>
<string name="frontImageDescription">表面</string>
<string name="importStocardMessage">Stocardでエクスポートした<i>***-sync.zip</i>ファイルを選択し、手動でバーコード形式を選択してください。
<string name="importStocardMessage">Stocardでエクスポートした<i>***-sync.zip</i>ファイルを選択してください。
\nファイルがない場合、e-mailing support@stocardapp.comにデータのエクスポートを要求してください。</string>
<string name="importStocard">Stocardからインポート</string>
<plurals name="selectedCardCount">
@@ -165,4 +165,10 @@
</plurals>
<string name="deleteConfirmation">このカードを削除しますか?</string>
<string name="deleteTitle">カードの削除</string>
<plurals name="deleteCardsConfirmation">
<item quantity="other"><xliff:g>%d</xliff:g> 枚のカードを削除しますか?</item>
</plurals>
<plurals name="deleteCardsTitle">
<item quantity="other">削除対象 <xliff:g>%d</xliff:g> cards</item>
</plurals>
</resources>

View File

@@ -179,4 +179,6 @@
<item quantity="few">Ištrinti <xliff:g>%d</xliff:g> korteles</item>
<item quantity="other">Ištrinti <xliff:g>%d</xliff:g> kortelių</item>
</plurals>
<string name="settings_system_locale">Sistemos</string>
<string name="settings_locale">Kalba</string>
</resources>

View File

@@ -175,4 +175,6 @@
<item quantity="one">Slett <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> kort</item>
<item quantity="other">Slett <xliff:g xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">%d</xliff:g> kort</item>
</plurals>
</resources>
<string name="settings_system_locale">System</string>
<string name="settings_locale">Språk</string>
</resources>

View File

@@ -175,4 +175,6 @@
<item quantity="one">Weet je zeker dat je deze <xliff:g>%d</xliff:g> kaart wilt verwijderen\?</item>
<item quantity="other">Weet je zeker dat je deze <xliff:g>%d</xliff:g> kaarten wilt verwijderen\?</item>
</plurals>
<string name="settings_system_locale">Systeemtaal</string>
<string name="settings_locale">Taal</string>
</resources>

View File

@@ -183,4 +183,6 @@
<item quantity="many">Удаление <xliff:g>%d</xliff:g> карт</item>
<item quantity="other">Удаление <xliff:g>%d</xliff:g> карт</item>
</plurals>
<string name="settings_system_locale">Системный</string>
<string name="settings_locale">Язык</string>
</resources>

View File

@@ -0,0 +1,179 @@
<?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="settings_lock_barcode_orientation">Lås streckkodens riktning</string>
<string name="settings_display_barcode_max_brightness">Gör streckkodsvyn ljusare</string>
<string name="settings_max_font_size_scale">Max. teckenstorlek</string>
<string name="settings_keep_screen_on">Håll skärmen påslagen</string>
<string name="enterBarcodeInstructions">Ange kortets ID och välj antingen streckkodstyp nedan, eller \"Detta kort har ingen streckkod\".</string>
<string name="importFidmeMessage">Välj den exporterade <i>fidme-export-request-xxxxxx.zip</i> från FidMe som du vill importera och välj streckkodstyper manuellt efteråt,
\neller skapa en från din FidMe-profil först genom att välja \"Data Protection\" och sen trycka på \"Extract my data\".</string>
<string name="importOptionApplicationExplanation">Använd vilken app som helst eller den filhanterare du föredrar för att öppna en fil.</string>
<string name="importOptionApplicationButton">Använd en annan app</string>
<string name="intent_import_card_from_url_share_text">Jag vill dela ett kort med dig</string>
<string name="noGroups">Klicka på plus(+)-knappen för att lägga till kategorigrupper först.</string>
<string name="groupsList">Grupper: <xliff:g>%s</xliff:g></string>
<string name="errorReadingImage">Kunde inte läsa bilden</string>
<string name="currency">Valuta</string>
<string name="failedGeneratingShareURL">Kunde inte skapa en delningsbar URL. Var snäll och rapportera detta.</string>
<string name="barcodeImageDescription">Bild av kortets streckkod</string>
<string name="card_ids_copied">Kopierade kort ID:n</string>
<plurals name="selectedCardCount">
<item quantity="one"><xliff:g>%d</xliff:g> kort valt</item>
<item quantity="other"><xliff:g>%d</xliff:g> kort valda</item>
</plurals>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="importLoyaltyCardKeychainMessage">Välj den exporterade <i>LoyaltyCardKeychain.csv</i> från Loyalty Card Keychain som du vill importera,
\neller skapa en från Import/Export-menyn i Loyalty Card Keychain först genom att trycka på Exportera.</string>
<string name="importVoucherVaultMessage">Välj den exporterade <i>vouchervault.json</i> från Voucher Vault som du vill importera,
\neller skapa en först genom att trycka på Exportera i Voucher Vault.</string>
<string name="importStocardMessage">Välj den exporterade <i>***-sync.zip</i> från Stocard som du vill importera,
\neller skaffa en först genom att skicka e-post till support@stocardapp.com och be om att få dina data exporterade.</string>
<string name="enter_group_name">Ange gruppnamn</string>
<string name="groups">Grupper</string>
<plurals name="groupCardCount">
<item quantity="one"><xliff:g>%d</xliff:g> kort</item>
<item quantity="other"><xliff:g>%d</xliff:g> kort</item>
</plurals>
<string name="all">Alla</string>
<string name="deleteConfirmationGroup">Ta bort grupp\?</string>
<string name="failedOpeningFileManager">Installera en filhanterare först.</string>
<string name="moveUp">Flytta uppåt</string>
<string name="moveDown">Flytta nedåt</string>
<string name="leaveWithoutSaveTitle">Avsluta</string>
<string name="leaveWithoutSaveConfirmation">Avsluta utan att spara\?</string>
<string name="addManually">Ange kort-ID manuellt</string>
<string name="addFromImage">Välj bild från galleriet</string>
<string name="balancePoints"><xliff:g>%s</xliff:g> poäng</string>
<string name="card">Kort</string>
<string name="barcode">Streckkod</string>
<string name="editBarcode">Redigera streckkod</string>
<string name="never">Aldrig</string>
<string name="moveBarcodeToTopOfScreen">Flytta streckkoden högst upp på skärmen</string>
<string name="moveBarcodeToCenterOfScreen">Centrera streckkoden på skärmen</string>
<string name="noBarcodeFound">Hittade ingen streckkod</string>
<string name="points">Poäng</string>
<string name="chooseImportType">Importera data från...\?</string>
<string name="accept">Acceptera</string>
<string name="importCatima">Importera från Catima</string>
<string name="importFidme">Importera från FidMe</string>
<string name="importLoyaltyCardKeychain">Importera från Loyalty Card Keychain</string>
<string name="importStocard">Importera from Stocard</string>
<string name="importVoucherVault">Importera från Voucher Vault</string>
<string name="barcodeId">Streckkodsvärde</string>
<string name="passwordRequired">Var snäll och ange lösenordet</string>
<string name="no">Nej</string>
<string name="yes">Ja</string>
<string name="updateBarcodeQuestionText">Du ändrade kortets ID. Vill du uppdatera så att streckkoden också använder samma värde\?</string>
<string name="updateBarcodeQuestionTitle">Uppdatera streckkodsvärde\?</string>
<string name="takePhoto">Ta ett foto</string>
<string name="removeImage">Ta bort bild</string>
<string name="setBackImage">Ange baksidesbild</string>
<string name="setFrontImage">Ange framsidesbild</string>
<string name="photos">Foton</string>
<string name="backImageDescription">Bild av kortets baksida</string>
<string name="frontImageDescription">Bild av kortets framsida</string>
<string name="intent_import_card_from_url_share_multiple_text">Jag vill dela några kort med dig</string>
<string name="copy_to_clipboard_multiple_toast">Kort-ID:n kopierades till Urklipp</string>
<string name="wrongValueForBarcodeType">Värdet är inte giltigt för den valda streckkodstypen</string>
<string name="unsupportedBarcodeType">Denna streckkodstyp kan ännu inte visas. Den kan komma att stödjas i en senare version av appen.</string>
<string name="setBarcodeId">Ange streckkodsvärde</string>
<string name="sameAsCardId">Samma som kort-ID</string>
<string name="turn_flashlight_on">Sätt på ficklampa</string>
<string name="turn_flashlight_off">Stäng av ficklampa</string>
<string name="settings_dark_theme">Mörkt</string>
<string name="settings_light_theme">Ljust</string>
<string name="settings_theme">Tema</string>
<string name="settings_category_title_ui">Användargränssnitt</string>
<string name="settings">Inställningar</string>
<string name="thumbnailDescription">Miniatyrbild av kort</string>
<string name="copy_to_clipboard_toast">Kort-ID kopierat till Urklipp</string>
<string name="selectBarcodeTitle">Välj streckkod</string>
<string name="debug_version_fmt">Version: <xliff:g id="version">%s</xliff:g></string>
<string name="about_title_fmt">Om <xliff:g id="app_name">%s</xliff:g></string>
<string name="app_copyright_old">Baserad på Loyalty Card Keychain
\ncopyright © 20162020 Branden Archer.</string>
<string name="app_copyright_fmt" tools:ignore="PluralsCandidate">Copyright © 2019<xliff:g>%d</xliff:g> Sylvia van Os.</string>
<string name="about">Om</string>
<string name="importOptionApplicationTitle">Använd en annan app</string>
<string name="importOptionFilesystemButton">Från filsystemet</string>
<string name="importOptionFilesystemExplanation">Välj en specifik fil från filsystemet.</string>
<string name="importOptionFilesystemTitle">Importera från filsystem</string>
<string name="exporting">Exporterar…</string>
<string name="importing">Importerar…</string>
<string name="exportFailed">Kunde inte exportera kort</string>
<string name="importFailed">Kunde inte importera kort</string>
<string name="importSuccessfulTitle">Importen lyckades</string>
<string name="importFailedTitle">Importen misslyckades</string>
<string name="importExportHelp">Säkerhetskopiering av dina kort låter dig flytta dem till en annan enhet.</string>
<string name="importExport">Importera/Exportera</string>
<string name="noCardExistsError">Kunde inte hitta kort</string>
<string name="noCardIdError">Inget kort-ID har angivits</string>
<string name="noStoreError">Inget namn har angivits</string>
<string name="noCardsMessage">Lägg till ett kort först</string>
<string name="cardShortcut">Kort-genväg</string>
<string name="addCardTitle">Lägg till kort</string>
<string name="editCardTitle">Redigera kort</string>
<string name="sendLabel">Skicka…</string>
<string name="share">Dela</string>
<string name="copy_to_clipboard">Kopiera ID till Urklipp</string>
<string name="ok">OK</string>
<plurals name="deleteCardsConfirmation">
<item quantity="one">Ta bort detta <xliff:g>%d</xliff:g> kort permanent\?</item>
<item quantity="other">Ta bort dessa <xliff:g>%d</xliff:g> kort permanent\?</item>
</plurals>
<string name="deleteConfirmation">Ta bort detta kort permanent\?</string>
<plurals name="deleteCardsTitle">
<item quantity="one">Ta bort <xliff:g>%d</xliff:g> kort</item>
<item quantity="other">Ta bort <xliff:g>%d</xliff:g> kort</item>
</plurals>
<string name="deleteTitle">Ta bort kort</string>
<string name="confirm">Bekräfta</string>
<string name="delete">Ta bort</string>
<string name="edit">Redigera</string>
<string name="save">Spara</string>
<string name="cancel">Avbryt</string>
<string name="unstar">Ta bort från favoriter</string>
<string name="star">Lägg till i favoriter</string>
<string name="noBarcode">Ingen streckkod</string>
<string name="barcodeNoBarcode">Detta kort har ingen streckkod</string>
<string name="barcodeType">Streckkodstyp</string>
<string name="cardId">Kort-ID</string>
<string name="storeName">Namn</string>
<string name="noMatchingGiftCards">Hittade ingenting. Försök justera din sökning.</string>
<string name="noGiftCards">Klicka på plus(+)-knappen för att lägga till ett kort, eller importera några från ⋮-menyn först.</string>
<string name="action_add">Lägg till</string>
<string name="action_search">Sök</string>
<string name="exportOptionExplanation">Datan kommer att sparas till en plats som du väljer.</string>
<string name="exportName">Exportera</string>
<string name="exportFailedTitle">Exporten misslyckades</string>
<string name="exportSuccessfulTitle">Exporten lyckades</string>
<string name="scanCardBarcode">Skanna streckkod på kort</string>
<string name="settings_system_theme">Systemtemat</string>
<string name="app_revision_fmt">Ändringsinfo: <xliff:g id="app_revision_url">%s</xliff:g></string>
<string name="privacy_policy_popup_text">Notis rörande integritetspolicy (krävs av vissa appbutiker):
\n
\nINGEN DATA ALLS SAMLAS IN, vilket vem som helst kan bekräfta eftersom vår app är fri programvara.</string>
<string name="unlockScreen">Tillåt rotation</string>
<string name="privacy_policy">Integritetspolicy</string>
<string name="expiryStateSentenceExpired">Förföll: <xliff:g>%s</xliff:g></string>
<string name="chooseExpiryDate">Välj förfallodatum</string>
<string name="expiryDate">Förfallodatum</string>
<string name="starImage">Favoritstjärna</string>
<string name="app_license">Copyleftad fri programvara, under GPLv3+-licens.</string>
<string name="app_resources">Fria tredjepartsresurser: <xliff:g id="app_resources_list">%s</xliff:g></string>
<string name="app_libraries">Fria tredjepartsbibliotek: <xliff:g id="app_libraries_list">%s</xliff:g></string>
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> verkar inte vara ett giltigt saldo.</string>
<string name="balance">Saldo</string>
<string name="balanceSentence">Saldo: <xliff:g>%s</xliff:g></string>
<string name="settings_disable_lockscreen_while_viewing_card">Förhindra skärmlåsning</string>
<string name="lockScreen">Förhindra rotation</string>
<string name="importCatimaMessage">Välj den exporterade <i>catima.zip</i> från Catima som du vill importera,
\neller skapa en från Import/Export-menyn i en annan Catima-app genom att trycka på Exportera där först.</string>
<string name="expiryStateSentence">Giltigt till: <xliff:g>%s</xliff:g></string>
<string name="exportSuccessful">Kortdata exporterad</string>
<string name="importSuccessful">Kortdata importerad</string>
<string name="failedParsingImportUriError">Kunde inte tolka import-URI:n</string>
<string name="note">Anteckning</string>
<string name="settings_system_locale">System</string>
<string name="settings_locale">Språk</string>
</resources>

View File

@@ -41,7 +41,7 @@
<string name="importCatima">Імпорт з Catima</string>
<string name="accept">Прийняти</string>
<string name="privacy_policy">Політика конфіденційності</string>
<string name="app_loyalty_card_keychain">Loyalty Card Keychain</string>
<string name="app_loyalty_card_keychain">Брелок картки лояльності</string>
<string name="chooseImportType">Імпортувати дані з\?</string>
<string name="parsingBalanceFailed"><xliff:g>%s</xliff:g> здається, не є дійсним залишком.</string>
<string name="points">Бали</string>
@@ -140,7 +140,7 @@
<string name="delete">Видалити</string>
<string name="edit">Редагувати</string>
<string name="save">Зберегти</string>
<string name="cancel">Відмінити</string>
<string name="cancel">Скасувати</string>
<string name="unstar">Видалити з улюблених</string>
<string name="star">Додати до улюблених</string>
<string name="cardId">ID картки</string>
@@ -160,8 +160,8 @@
<string name="photos">Фотографії</string>
<string name="backImageDescription">Тильна сторона карти</string>
<string name="frontImageDescription">Лицьова сторона карти</string>
<string name="importStocardMessage">Оберіть Stocard експорт-файл, названий <i>***-sync.zip</i> для імпортування і оберіть типи штрих-кодів вручну пізніше.
\nЧи отримайте його надіславши електронного листа із запитом на експортування ваших даних до support@stocardapp.com.</string>
<string name="importStocardMessage">Виберіть експорт <i> ***-sync.zip </i> зі Stocard для імпорту.
\nАбо отримайте його, надіславши електронною поштою support@stocardapp.com з проханням експортувати ваші дані.</string>
<string name="importStocard">Імпорт із Stocard</string>
<plurals name="selectedCardCount">
<item quantity="one">Обрано: <xliff:g>%d</xliff:g> карта</item>
@@ -169,6 +169,18 @@
<item quantity="many">Обрано: <xliff:g>%d</xliff:g> карт</item>
<item quantity="other">Обрано: <xliff:g>%d</xliff:g> карт</item>
</plurals>
<string name="deleteTitle">Видалити карту</string>
<string name="deleteTitle">Видалити картку</string>
<string name="deleteConfirmation">Бажаєте видалити карту\?</string>
</resources>
<plurals name="deleteCardsTitle">
<item quantity="one">Видалити <xliff:g>%d</xliff:g> картку</item>
<item quantity="few">Видалити <xliff:g>%d</xliff:g> картки</item>
<item quantity="many">Видалити <xliff:g>%d</xliff:g> картки</item>
<item quantity="other">Видалити <xliff:g>%d</xliff:g> картки</item>
</plurals>
<plurals name="deleteCardsConfirmation">
<item quantity="one">Видаліть цю <xliff:g>%d</xliff:g> карту назавжди\?</item>
<item quantity="few">Видаліть ці <xliff:g>%d</xliff:g> карти назавжди\?</item>
<item quantity="many">Видаліть ці <xliff:g>%d</xliff:g> карти назавжди\?</item>
<item quantity="other">Видаліть ці <xliff:g>%d</xliff:g> карти назавжди\?</item>
</plurals>
</resources>

View File

@@ -24,16 +24,16 @@
<string name="setBarcodeId">设置条形码值</string>
<string name="sameAsCardId">与卡号相同</string>
<string name="barcodeId">条形码值</string>
<string name="importVoucherVaultMessage">找到一个可能名为<i>vouchervault.json</i>的文件进行导入。
<string name="importVoucherVaultMessage">选择从Voucher Vault导出的文件<i>vouchervault.json</i>进行导入。
\n或者先在Voucher Vault中按下导出键来创建导出。</string>
<string name="importVoucherVault">从 Voucher Vault 导入</string>
<string name="importLoyaltyCardKeychainMessage">找到一个很可能名为<i>LoyaltyCardKeychain.csv</i>的文件来导入。
<string name="importLoyaltyCardKeychainMessage">选择从Loyalty Card Keychain导出的文件<i>LoyaltyCardKeychain.csv</i>来导入。
\n或者先从Loyalty Card Keychain的Import/Export菜单中选择Export来导出。</string>
<string name="importLoyaltyCardKeychain">从 Loyalty Card Keychain 导入</string>
<string name="importFidmeMessage">找到一个可能名为<i>fidme-export-request-xxxxxx.zip</i>的文件进行导入,之后再手动选择条码类型。
<string name="importFidmeMessage">选择从Fidme导出的文件<i>fidme-export-request-xxxxxx.zip</i>进行导入,之后再手动选择条码类型。
\n或者从你的FidMe配置文件中选择数据保护然后按提取我的数据来创建导出。</string>
<string name="importFidme">从 FidMe 导入</string>
<string name="importCatimaMessage">找到一个可能名为<i>Catima.csv</i>的文件来导入。
<string name="importCatimaMessage">选择从Catima导出的文件<i>Catima.zip</i>来导入。
\n或者先从另一个Catima应用程序的导入/导出菜单中,来创建导出。</string>
<string name="importCatima">从 Catima 导入</string>
<string name="accept">接受</string>
@@ -143,4 +143,32 @@
<string name="action_search">搜索</string>
<string name="deleteConfirmation">删除此卡?</string>
<string name="deleteTitle">移除卡片</string>
<string name="starImage">最喜欢的星星</string>
<string name="importStocardMessage">选择你从Stocard导出的<i>****-sync.zip</i>来导入。
\n或者发电子邮件给support@stocardapp.com要求导出你的数据从而获得此文件。</string>
<plurals name="deleteCardsConfirmation">
<item quantity="other">确定永久删除 <xliff:g>%d</xliff:g> 这些卡片?</item>
</plurals>
<plurals name="selectedCardCount">
<item quantity="other">已选择 <xliff:g>%d</xliff:g> 张卡片</item>
</plurals>
<plurals name="deleteCardsTitle">
<item quantity="other">删除 <xliff:g>%d</xliff:g> 张卡片</item>
</plurals>
<string name="turn_flashlight_off">关上手电筒</string>
<string name="turn_flashlight_on">开启手电筒</string>
<string name="failedGeneratingShareURL">无法生成可分享的URL。请向开发者报告。</string>
<string name="passwordRequired">请输入密码</string>
<string name="no"></string>
<string name="yes"></string>
<string name="updateBarcodeQuestionText">您改变了卡片的ID。是否需要同时更新条形码以使用相同的值</string>
<string name="updateBarcodeQuestionTitle">是否要更新条形码的值?</string>
<string name="takePhoto">拍照</string>
<string name="removeImage">移除图像</string>
<string name="setBackImage">设置背面图像</string>
<string name="setFrontImage">设置正面图像</string>
<string name="photos">照片</string>
<string name="backImageDescription">卡片的背面图像</string>
<string name="frontImageDescription">卡片的正面图像</string>
<string name="importStocard">从Stocard导入</string>
</resources>

View File

@@ -12,6 +12,34 @@
<item>@string/settings_dark_theme</item>
</string-array>
<string-array name="locale_values">
<item />
<item>bg</item>
<item>cs</item>
<item>de</item>
<item>el-rGR</item>
<item>eo</item>
<item>es-rAR</item>
<item>es</item>
<item>fi</item>
<item>fr</item>
<item>he-rIL</item>
<item>it</item>
<item>ja</item>
<item>ko</item>
<item>lt</item>
<item>nb-rNO</item>
<item>nl</item>
<item>oc</item>
<item>pl</item>
<item>ro-rRO</item>
<item>ru</item>
<item>sk</item>
<item>sl</item>
<item>uk</item>
<item>zh-rCN</item>
</string-array>
<array name="letter_tile_colors">
<item>#f16364</item>
<item>#f58559</item>

View File

@@ -213,4 +213,7 @@
<string name="failedGeneratingShareURL">Could not generate sharable URL. Please report this.</string>
<string name="turn_flashlight_on">Turn flashlight on</string>
<string name="turn_flashlight_off">Turn flashlight off</string>
<string name="settings_locale">Language</string>
<string name="settings_key_locale" translatable="false">pref_locale</string>
<string name="settings_system_locale">System</string>
</resources>

View File

@@ -1,5 +1,4 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
@@ -17,6 +16,10 @@
<item name="windowNoTitle">true</item>
</style>
<style name="SplashTheme" parent="@style/AppTheme">
<item name="android:windowBackground">@drawable/background_splash</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light"/>

View File

@@ -16,6 +16,15 @@
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<ListPreference
android:key="@string/settings_key_locale"
android:title="@string/settings_locale"
android:defaultValue=""
android:entries="@array/locale_values"
android:entryValues="@array/locale_values"
app:iconSpaceReserved="false"
app:singleLineTitle="false" />
<nl.invissvenska.numberpickerpreference.NumberDialogPreference
android:key="@string/settings_key_max_font_size_scale"
android:title="@string/settings_max_font_size_scale"

View File

@@ -344,7 +344,7 @@ public class LoyaltyCardViewActivityTest
}
@Test
public void noDataLossOnResume()
public void noDataLossOnResumeOrRotate()
{
registerMediaStoreIntentHandler();
@@ -421,6 +421,22 @@ public class LoyaltyCardViewActivityTest
// Check if no changes lost
checkAllFields(activity, newCard ? ViewMode.ADD_CARD : ViewMode.UPDATE_CARD, "correct store", "correct note", DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate), "100.00", currency.getSymbol(), "12345678", "87654321", BarcodeFormat.QR_CODE.name(), frontBitmap, backBitmap);
// Rotate to landscape
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
activity.recreate();
shadowOf(getMainLooper()).idle();
// Check if no changes lost
checkAllFields(activity, newCard ? ViewMode.ADD_CARD : ViewMode.UPDATE_CARD, "correct store", "correct note", DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate), "100.00", currency.getSymbol(), "12345678", "87654321", BarcodeFormat.QR_CODE.name(), frontBitmap, backBitmap);
// Rotate to portrait
shadowOf(getMainLooper()).idle();
activity.recreate();
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Check if no changes lost
checkAllFields(activity, newCard ? ViewMode.ADD_CARD : ViewMode.UPDATE_CARD, "correct store", "correct note", DateFormat.getDateInstance(DateFormat.LONG).format(expiryDate), "100.00", currency.getSymbol(), "12345678", "87654321", BarcodeFormat.QR_CODE.name(), frontBitmap, backBitmap);
}
}

View File

@@ -3,7 +3,7 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
gradlePluginPortal()
}
@@ -20,6 +20,7 @@ buildscript {
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
gradlePluginPortal()
}

View File

@@ -1 +0,0 @@
catima.app

View File

@@ -1 +0,0 @@
theme: jekyll-theme-cayman

View File

@@ -1 +0,0 @@
README.md

View File

@@ -1,70 +0,0 @@
# PRIVACY POLICY FOR CATIMA
This privacy policy governs your use of the software application Catima (“Application”) for mobile devices
that was created by Sylvia van Os. The Application is designed to store and display barcodes.
# What information does the Application obtain and how is it used?
The Application allows a user to create store entries with a barcode. When a store is later selected the
corresponding barcode is displayed. This user data remains on the device and is not transmitted off of the
device.
If a crash occurs, a dialog may appear with crash details and a suggestion to transmit the details
to the developer. A user has the option to not send the crash details; a user must 'opt-in' to
sending such details.
# User Provided Information
The Application does not have a registration option. The only information which is obtained
is (1) crash details which a user intentionally send to the developer via the app, and
(2) any information provided by the App Store that the application was downloaded from.
# Automatically Collected Information
No information is automatically collected from the Application and transmitted off of the mobile device,
either automatically or manually.
# Does the Application collect precise real time location information of the device?
No.
# Do third parties see and/or have access to information obtained by the Application?
Any crash data which is intentionally submitted by a user to the developer is stored on an email server
which is not owned by the developer. Crash details may be uploaded to a third-party development tool
such as GitHub. In such a case the email address of the user is not uploaded, unless specifically requested
by the user. Besides this, no other data is transmitted off of the mobile device by the Application.
# What are my opt-out rights?
The only data collection is that of crash details, and submitting these to the developer is only opt-in. To
opt-out, a user would have to not submit crash details via the app.
# Data Retention Policy, Managing Your Information
Crash details submitted by a user are stored on a third-party email service, and the retention policy is to
not delete such emails.
# Children
This Application is not used to knowingly solicit data from or market to children under the age of 13. If a parent or
guardian becomes aware that his or her child has provided us with information without their consent, they should
contact us at sylvia+googleplay@hackerchick.me. We will delete such information from our files within a reasonable time.
# Security
We are concerned about safeguarding the confidentiality of your information. The only information which could
be sent to the developer is crash details, and these are opt-in. If a user is presented with the option to
submit details on a crash, all the information which would be submitted is presented to the user. The only
personally identifiable information is the email address of the user which is used to send the crash details.
# Changes
This Privacy Policy may be updated from time to time for any reason. We will notify you of any changes to our
Privacy Policy by posting the new Privacy Policy here. You are advised to consult this Privacy Policy regularly
for any changes, as continued use is deemed approval of all changes. You can check the history of this policy by
checking this history of this file in GitHub.
# Contact us
If you have any questions regarding privacy while using the Application, or have questions about our practices,
please contact us via email at sylvia+googleplay@hackerchick.me.

View File

@@ -1,10 +0,0 @@
# Shared Card
Someone wants to share a card with you. To import this card, you will first need to install the Catima app. It is free, Open Source and contains no ads.
<a href="https://play.google.com/store/apps/details?id=me.hackerchick.catima" target="_blank">
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="90"/></a>
<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>
After installing the app, just click the link you were given again and choose "Import into Catima".

View File

@@ -0,0 +1,23 @@
Спрете да търсите пластмасови карти за възнаграждение по време на плащане в магазин или в интернет магазин.
<b>Сканирайте баркодовете на устройството си с помощта на неговата камера и забравете за картите.</b>
😺
Забравете за портфейла си или го запазете ултралек за ценни вещи.
😺
С този основен инструмент за ежедневно носене (EDC) можете да замените безполезната пластмаса с пари в брой.
😺
- Избягвайте шпионирането с много малко разрешения. Без достъп до интернет и без реклами.
- Добавете карти или кодове с имена и цветове, които можете да персонализирате.
- Ръчно въвеждане на код, ако няма баркод за съхранение или той не може да се използва.
- Импортиране на карти и кодове от файлове, Catima, FidMe, Loyalty Card Keychain, Stocard и Voucher Vault.
- Направете резервно копие на всичките си карти и ги прехвърлете на ново устройство, ако искате.
- Споделяйте купони, ексклузивни оферти, промоционални кодове или карти и кодове, като използвате всяко приложение.
- Тъмна тема и опции за достъпност за потребители с увредено зрение.
- Направено за всички от общността на свободния софтуер.
- Локализирани ръчно направени преводи за над 20 езика.
- Безвъзмездно, подкрепено от приноса на общността.
- Използвайте, изучавайте, променяйте и споделяйте, както желаете; <i>с всички</i>.
- Не само свободен софтуер / отворен код. <i>Copylefted</i> либре софтуер (GPLv3+) за управление на карти.
😺
Опростете живота и пазаруването си и никога повече не губете хартиена касова бележка, карта за подарък за плащане в магазина или самолетен билет.
Вземете всичките си награди и бонуси със себе си и спестявайте, докато пътувате.
😺

View File

@@ -0,0 +1 @@
За вашите баркодове, членства, програми за лоялност, талони и билети.

View File

@@ -0,0 +1 @@
Catima - Портфейлът за карти Libre

View File

@@ -1,23 +1,23 @@
Lopeta muovisten etukorttien etsiminen kaupan tai verkkokaupan kassalla.
Lopeta muovisten palkintokorttien etsiminen kaupan tai verkkokaupan kassalla.
<b>Skannaa viivakoodit laitteeseesi sen kameran avulla, unohda kortit.</b>
😺
Unohda lompakko tai pidä se ultrakevyenä sinulle arvokkaita asioita varten.
Unohda lompakko tai pidä se ultrakevyesti arvoesineitä varten.
😺
Tämän jokapäiväisen (EDC) työkalun avulla, voit korvata turhan muovin käteisellä.
Tämän välttämättömän jokapäiväisen kantamisen (EDC) välineen avulla voit korvata turhan muovin käteisellä.
😺
- Vältä vakoilua hyvin vähäisillä käyttöoikeuksilla. Ei Internet-yhteyttä eikä mainoksia.
- Lisää kortteja tai koodeja nimettynä ja muokattavilla väreillä.
- Koodin syöttäminen manuaalisesti, jos tallennettavaa viivakoodia ei ole tai sitä ei voida käyttää.
- Tuo kortteja ja koodeja varmuuskopiotiedostoista, Catima, Loyalty Card Keychain, Voucher Vault, ja FidMe.
- Vältä vakoilu hyvin vähillä luvilla. Ei Internet-yhteyttä eikä mainoksia.
- Lisää kortteja tai koodeja nimillä ja muokattavilla väreillä.
- Koodin syöttäminen manuaalisesti, jos viivakoodia ei voi tallentaa tai sitä ei voi käyttää.
- Tuo kortteja ja koodeja tiedostoista, Catimasta, FidMe:stä, kanta-asiakaskorttiavaimenperästä, Stocardista ja Voucher Holvista.
- Tee varmuuskopio kaikista korteistasi ja siirrä ne halutessasi uuteen laitteeseen.
- Jaa kuponkeja, erikoistarjouksia, tarjouskoodeja tai kortteja ja koodeja millä tahansa sovelluksella.
- Tumma teema ja esteettömän käytön mahdollisuus näkövammaisille käyttäjille.
- Vapaan ohjelmistoyhteisön kaikkia varten tekemä.
- Jaa kuponkeja, yksinoikeustarjouksia, tarjouskoodeja tai kortteja ja koodeja millä tahansa sovelluksella.
- Tumma teema ja saavutettavuusvaihtoehdot näkövammaisille käyttäjille.
- Libre-ohjelmistoyhteisön kaikille tekemä.
- Lokalisoidut käsintehdyt käännökset yli 20 kielelle.
- Vapaa, yhteisön tukema.
- Käytä, opiskele, muuta ja jaa sitä toiveidesi mukaan; <i>kaikille</i>.
- Ei vain vapaa ohjelma / Avoin lähdekoodi. <i>Copyleft</i> vapaa ohjelma (GPLv3+) korttien hallinta.
- Gratis, yhteisön panoksilla tuettu.
- Käytä, tutki, muuta ja jaa sitä haluamallasi tavalla; <i>kaikkien</i> kanssa.
- Ei vain vapaat ohjelmistot / avoin lähdekoodi. <i>Copylefted</i> libre-ohjelmisto (GPLv3+) kortinhallinta.
😺
Yksinkertaista elämääsi ja ostosten tekemistä, äläkä enää koskaan hukkaa paperikuittia, kaupassa maksettavaa lahjakorttia tai lentolippua.
Ota kaikki edut ja bonukset mukaasi ja säästä missä menetkin.
Yksinkertaista elämääsi ja shoppailuasi, äläkä enää koskaan hukkaa paperikuittia, kaupassa maksettavaa lahjakorttia tai lentolippua.
Ota kaikki palkintosi ja bonuksesi mukaasi ja säästä matkan aikana.
😺

View File

@@ -0,0 +1 @@
För dina streckkoder, medlemskap, kundklubbar, kuponger och biljetter.

View File

@@ -0,0 +1 @@
Catima — Den fria kortplånboken

View File

@@ -0,0 +1,23 @@
在实体店或网店结账时,再也不用翻找塑料积分卡了。
<b>用摄像头扫描条形码存储到您的设备,就不需要卡片了。</b>
😺
抛开钱包,或者只携带贵重物品给钱包减负。
😺
有了这个必不可少的日常携带EDC工具你可以用现金取代无用的塑料。
😺
- 要求权限极少,避免隐私泄露。无需互联网接入,没有广告。
- 添加卡片或号码,并为其设置名称和颜色。
- 如果没有条码可存储,或条码无法使用,可手动输入号码。
- 可以从本地文件、Catima、FidMe、Loyalty Card Keychain、Stocard和Voucher Vault导入卡片和号码。
- 对所有的卡进行备份。可以把备份转移到新设备上。
- 把优惠券、促销码、卡片和号码等等分享到任何应用程序。
- 为视力受损的用户提供暗色主题和无障碍选项。
- 自由软件社区为大家制作。
- 提供20多种语言的本地化人工翻译。
- 免费的,由社区贡献支持。
- 欢迎<i>与所有人</i>随意使用、研究、改变和分享本软件。
- 不仅是自由软件/开放源码。<i>Copylefted</i>自由软件GPLv3+)卡片管理。
😺
简化你的生活和购物,再也不会丢失纸质收据、店内支付礼品卡或飞机票。
带上你所有的优惠和积分卡,随时随地省钱。
😺

View File

@@ -0,0 +1 @@
适用于您的条形码、会员卡、积分卡、优惠券和门票等。

View File

@@ -0,0 +1 @@
Catima - 自由卡片钱包