Android Power Screen `Control` always takes the same intent

88 Views Asked by At

I am cooperating with TheLastProject@github on an app that works as a loyalty card wallet: Catima (link to my fork and to app listing)

I tried to implement Power screen cards and I followed the Android development guide on Device Controls

The resulting code is (this link could be invalidated during a rebase) has prblems

@NonNull
@Override
public Flow.Publisher<Control> createPublisherFor(@NonNull List<String> controlIds) {
    return subscriber -> {
        subscriber.onSubscribe(new NoOpSubscription());
        for (String controlId : controlIds) {
            Integer cardId = this.controlIdToCardId(controlId);
            if (cardId == null)
                continue;
            LoyaltyCard card = dbHelper.getLoyaltyCard(cardId);
            Intent openIntent = new Intent(this, LoyaltyCardViewActivity.class)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    .putExtra("id", card.id);
            PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, openIntent, PendingIntent.FLAG_IMMUTABLE);
            var ret = new Control.StatefulBuilder(controlId, pendingIntent)
                    .setTitle(card.store)
                    .setDeviceType(DeviceTypes.TYPE_GENERIC_OPEN_CLOSE)
                    .setSubtitle(card.note)
                    .setStatus(Control.STATUS_OK)
                    .setControlTemplate(new StatelessTemplate(controlId))
                    .setCustomIcon(Icon.createWithBitmap(Utils.generateIcon(this, card.store, card.headerColor).getLetterTile()))
                    .build();
            Log.d(TAG, "Dispatching widget " + controlId);
            subscriber.onNext(ret);
        }
        subscriber.onComplete();
    };
}

Code design

Loyalty cards are identified by an integer. I have generated unique control IDs based on that integer, so that I can query them. As told me by the Maintainer, LoyaltyCardViewActivity is the view that can be used to display a loyalty card, and the intent looks genuine.

So for each card in the input, I iterate and reactively emit a new Control for each card. Each control has its own intent and every intent is bound to the card ID.

Problem

The problem is that when I long-press on the widget, the intent emitted by Android carries the same card ID, which means that no matter I select different widgets, the same (not so deterministic) card is displayed.

"Not so deterministic". When I remove all controls and add them again, I can't predict which card is displayed. But every time I open the power menu again I get the same card.

In Android studio I debugged and I see the very same card ID passed to LoyaltyCardViewActivity, despite when I create the Control object I can clearly debug that the intent carries always a different ID

How to reproduce

Works on the emulator. I used a dump of real loyalty cards and an EU digital green certificate. But one could create two or more barcode cards by inputting random numbers.

Question

What's wrong with my code?

1

There are 1 best solutions below

1
CommonsWare On BEST ANSWER
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, openIntent, PendingIntent.FLAG_IMMUTABLE);

You are calling this N times, and so my guess is that you expect that you are creating N distinct PendingIntent objects. In reality, you are only creating one, because your PendingIntent identifier is the same for all of them: 0.

Change this to be something more like:

PendingIntent pendingIntent = PendingIntent.getActivity(this, cardId, openIntent, PendingIntent.FLAG_IMMUTABLE);

(I am guessing that cardId is unique for all your cards)