Merge pull request #2566 from realwk/widget

Widget with all cards
This commit is contained in:
Sylvia van Os
2025-07-31 22:35:44 +02:00
committed by GitHub
12 changed files with 279 additions and 16 deletions

View File

@@ -112,6 +112,7 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.core:core-ktx:1.16.0")
implementation("androidx.core:core-remoteviews:1.1.0")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.exifinterface:exifinterface:1.4.1")
implementation("androidx.palette:palette:1.0.0")

View File

@@ -30,6 +30,20 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:localeConfig="@xml/locales_config">
<receiver
android:name=".ListWidget"
android:label="@string/card_list_widget_name"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/list_widget_info" />
</receiver>
<activity
android:name=".MainActivity"
android:exported="true"

View File

@@ -0,0 +1,130 @@
package protect.card_locker
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.Build
import android.view.View
import android.widget.RemoteViews
import androidx.core.widget.RemoteViewsCompat
import protect.card_locker.DBHelper.LoyaltyCardArchiveFilter
class ListWidget : AppWidgetProvider() {
fun updateAll(context: Context) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val componentName = ComponentName(context, ListWidget::class.java)
onUpdate(
context,
appWidgetManager,
appWidgetManager.getAppWidgetIds(componentName)
)
}
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
val database = DBHelper(context).readableDatabase
// Get cards
val order = Utils.getLoyaltyCardOrder(context);
val orderDirection = Utils.getLoyaltyCardOrderDirection(context);
val loyaltyCardCursor = DBHelper.getLoyaltyCardCursor(
database,
"",
null,
order,
orderDirection,
LoyaltyCardArchiveFilter.Unarchived
)
// Bind every card to cell in the grid
var hasCards = false
val remoteCollectionItemsBuilder = RemoteViewsCompat.RemoteCollectionItems.Builder()
if (loyaltyCardCursor.moveToFirst()) {
do {
val loyaltyCard = LoyaltyCard.fromCursor(context, loyaltyCardCursor)
remoteCollectionItemsBuilder.addItem(
loyaltyCard.id.toLong(),
createRemoteViews(
context, loyaltyCard
)
)
hasCards = true
} while (loyaltyCardCursor.moveToNext())
}
loyaltyCardCursor.close()
// Create the base empty view
var views = RemoteViews(context.packageName, R.layout.list_widget_empty)
if (hasCards) {
// If we have cards, create the list
views = RemoteViews(context.packageName, R.layout.list_widget)
val templateIntent = Intent(context, LoyaltyCardViewActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
context,
0,
templateIntent,
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
views.setPendingIntentTemplate(R.id.grid_view, pendingIntent)
RemoteViewsCompat.setRemoteAdapter(
context,
views,
appWidgetId,
R.id.grid_view,
remoteCollectionItemsBuilder.build()
)
}
// Let Android know the widget is ready for display
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
private fun createRemoteViews(context: Context, loyaltyCard: LoyaltyCard): RemoteViews {
// Create a single cell for the grid view, bind it to open in the LoyaltyCardViewActivity
// Note: Android 5 will not use bitmaps
val remoteViews = RemoteViews(context.packageName, R.layout.list_widget_item).apply {
val headerColor = Utils.getHeaderColor(context, loyaltyCard)
val foreground = if (Utils.needsDarkForeground(headerColor)) Color.BLACK else Color.WHITE
setInt(R.id.item_container_foreground, "setBackgroundColor", headerColor)
val icon = loyaltyCard.getImageThumbnail(context)
// setImageViewIcon is not supported on Android 5, so force Android 5 down the text path
if (icon != null && Build.VERSION.SDK_INT >= 23) {
setInt(R.id.item_container_foreground, "setBackgroundColor", foreground)
setImageViewIcon(R.id.item_image, Icon.createWithBitmap(icon))
setViewVisibility(R.id.item_text, View.INVISIBLE)
setViewVisibility(R.id.item_image, View.VISIBLE)
} else {
setImageViewBitmap(R.id.item_image, null)
setTextViewText(R.id.item_text, loyaltyCard.store)
setViewVisibility(R.id.item_text, View.VISIBLE)
setViewVisibility(R.id.item_image, View.INVISIBLE)
setTextColor(
R.id.item_text,
foreground
)
}
// Add the card ID to the intent template
val fillInIntent = Intent().apply {
putExtra(LoyaltyCardViewActivity.BUNDLE_ID, loyaltyCard.id)
}
setOnClickFillInIntent(R.id.item_container, fillInIntent)
}
return remoteViews
}
}

View File

@@ -880,6 +880,8 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
} else if (id == R.id.action_star_unstar) {
DBHelper.updateLoyaltyCardStarStatus(database, loyaltyCardId, loyaltyCard.starStatus == 0 ? 1 : 0);
new ListWidget().updateAll(LoyaltyCardViewActivity.this);
// Re-init loyaltyCard with new data from DB
onResume();
invalidateOptionsMenu();
@@ -890,6 +892,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
Toast.makeText(LoyaltyCardViewActivity.this, R.string.archived, Toast.LENGTH_LONG).show();
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
new ListWidget().updateAll(LoyaltyCardViewActivity.this);
// Re-init loyaltyCard with new data from DB
onResume();
@@ -915,6 +918,7 @@ public class LoyaltyCardViewActivity extends CatimaAppCompatActivity implements
DBHelper.deleteLoyaltyCard(database, LoyaltyCardViewActivity.this, loyaltyCardId);
ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId);
new ListWidget().updateAll(LoyaltyCardViewActivity.this);
finish();
dialog.dismiss();

View File

@@ -2,6 +2,8 @@ package protect.card_locker;
import android.app.Activity;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -330,22 +332,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
selectedTab = activeTabPref.getInt(getString(R.string.sharedpreference_active_tab), 0);
// Restore sort preferences from Shared Preferences
// If one of the sorting prefererences has never been set or is set to an invalid value,
// stick to the defaults.
SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
getString(R.string.sharedpreference_sort),
Context.MODE_PRIVATE);
String orderString = sortPref.getString(getString(R.string.sharedpreference_sort_order), null);
String orderDirectionString = sortPref.getString(getString(R.string.sharedpreference_sort_direction), null);
if (orderString != null && orderDirectionString != null) {
try {
mOrder = DBHelper.LoyaltyCardOrder.valueOf(orderString);
mOrderDirection = DBHelper.LoyaltyCardOrderDirection.valueOf(orderDirectionString);
} catch (IllegalArgumentException ignored) {
}
}
mOrder = Utils.getLoyaltyCardOrder(this);
mOrderDirection = Utils.getLoyaltyCardOrderDirection(this);
mGroup = null;
@@ -442,6 +430,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
if (mCurrentActionMode != null) {
mCurrentActionMode.finish();
}
new ListWidget().updateAll(mAdapter.mContext);
}
private void processParseResultList(List<ParseResult> parseResultList, String group, boolean closeAppOnNoBarcode) {
@@ -709,6 +699,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard
showReversed.isChecked() ? DBHelper.LoyaltyCardOrderDirection.Descending : DBHelper.LoyaltyCardOrderDirection.Ascending
);
new ListWidget().updateAll(this);
dialog.dismiss();
});

View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -1176,4 +1177,40 @@ public class Utils {
return ImageView.ScaleType.FIT_CENTER;
}
public static DBHelper.LoyaltyCardOrder getLoyaltyCardOrder(Context context) {
SharedPreferences sortPref = context.getSharedPreferences(
"sharedpreference_sort",
Context.MODE_PRIVATE
);
String orderString = sortPref.getString("sharedpreference_sort_order", null);
if (orderString != null) {
try {
return DBHelper.LoyaltyCardOrder.valueOf(orderString);
} catch (IllegalArgumentException ignored) {
}
}
return DBHelper.LoyaltyCardOrder.Alpha;
}
public static DBHelper.LoyaltyCardOrderDirection getLoyaltyCardOrderDirection(Context context) {
SharedPreferences sortPref = context.getSharedPreferences(
"sharedpreference_sort",
Context.MODE_PRIVATE
);
String orderDirectionString = sortPref.getString("sharedpreference_sort_direction", null);
if (orderDirectionString != null) {
try {
return DBHelper.LoyaltyCardOrderDirection.valueOf(orderDirectionString);
} catch (IllegalArgumentException ignored) {
}
}
return DBHelper.LoyaltyCardOrderDirection.Ascending;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- android:columnWidth must be kept in sync with list_widget_item.xml -->
<GridView
android:id="@+id/grid_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:numColumns="auto_fit"
android:columnWidth="107dp"
android:verticalSpacing="4dp"
android:horizontalSpacing="4dp"
android:gravity="center"/>
</LinearLayout>

View File

@@ -0,0 +1,17 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/widget_layout"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/no_cards_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/card_list_widget_empty"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="12sp"
app:autoSizeMaxTextSize="100sp"
app:autoSizeStepGranularity="2sp" />
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
85.6dp : 53.98dp
Both multiplied by 1.25 to fit better
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="107dp"
android:layout_height="67.475dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center"
android:id="@+id/item_container"
android:background="@drawable/round_outline"
android:clipToOutline="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/item_container_foreground">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center|center" />
<TextView
android:id="@+id/item_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:autoSizeMinTextSize="6sp"
app:autoSizeTextType="uniform"
android:layout_gravity="center|center"
android:gravity="center" />
</FrameLayout>
</FrameLayout>

View File

@@ -362,5 +362,7 @@
<string name="unsupportedFile">This file is not supported</string>
<string name="generic_error_please_retry">Sorry, something went wrong, please try again...</string>
<string name="width">Width</string>
<string name="card_list_widget_name">Card list</string>
<string name="setBarcodeWidth">Set Barcode Width</string>
<string name="card_list_widget_empty">After you add some loyalty cards in Catima, they will appear here. If you have cards, make sure they are not all archived.</string>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/list_widget"
android:initialLayout="@layout/list_widget"
android:minWidth="245dp"
android:minHeight="54dp"
android:previewImage="@drawable/widget_preview"
android:resizeMode="horizontal|vertical"
android:targetCellWidth="4"
android:targetCellHeight="2"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen" />