Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4faf3db authored by Charles Wang's avatar Charles Wang
Browse files

Fixes bug where loyalty card is showing on quick access wallet screen.

Before/after screenshots of change: http://shortn/_BYxp8yQ9uo

Bug: b/282027184
Test: atest packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt;  atest QuickAccessWalletKeyguardQuickAffordanceConfigTest; atest QuickAccessWalletTileTest; atest WalletScreenControllerTest
Change-Id: Iaa76ebe80554a02407c002f91f45ba3763242b53
parent 3e4bbf4a
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.wallet.controller.QuickAccessWalletController
import com.android.systemui.wallet.controller.QuickAccessWalletController
import com.android.systemui.wallet.util.getPaymentCards
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
@@ -60,7 +61,7 @@ constructor(
            val callback =
            val callback =
                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                        val hasCards = response?.walletCards?.isNotEmpty() == true
                        val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
                        trySendWithFailureLogging(
                        trySendWithFailureLogging(
                            state(
                            state(
                                isFeatureEnabled = isWalletAvailable(),
                                isFeatureEnabled = isWalletAvailable(),
@@ -135,7 +136,7 @@ constructor(
                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                        continuation.resumeWith(
                        continuation.resumeWith(
                            Result.success(response?.walletCards ?: emptyList())
                            Result.success(getPaymentCards(response.walletCards) ?: emptyList())
                        )
                        )
                    }
                    }


+2 −1
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.graphics.drawable.Icon.TYPE_URI;
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;


import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;


import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
@@ -210,7 +211,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
        public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
        public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
            Log.i(TAG, "Successfully retrieved wallet cards.");
            Log.i(TAG, "Successfully retrieved wallet cards.");
            mIsWalletUpdating = false;
            mIsWalletUpdating = false;
            List<WalletCard> cards = response.getWalletCards();
            List<WalletCard> cards = getPaymentCards(response.getWalletCards());
            if (cards.isEmpty()) {
            if (cards.isEmpty()) {
                Log.d(TAG, "No wallet cards exist.");
                Log.d(TAG, "No wallet cards exist.");
                mCardViewDrawable = null;
                mCardViewDrawable = null;
+7 −16
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.systemui.wallet.ui;
package com.android.systemui.wallet.ui;


import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;

import android.app.PendingIntent;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -47,10 +49,10 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;


import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/** Controller for the wallet card carousel screen. */
/** Controller for the wallet card carousel screen. */
public class WalletScreenController implements
public class WalletScreenController implements
@@ -126,22 +128,11 @@ public class WalletScreenController implements
            return;
            return;
        }
        }
        Log.i(TAG, "Successfully retrieved wallet cards.");
        Log.i(TAG, "Successfully retrieved wallet cards.");
        List<WalletCard> walletCards = response.getWalletCards();
        List<WalletCard> walletCards = getPaymentCards(response.getWalletCards());

        boolean allUnknown = true;
        for (WalletCard card : walletCards) {
            if (card.getCardType() != WalletCard.CARD_TYPE_UNKNOWN) {
                allUnknown = false;
                break;
            }
        }


        List<WalletCardViewInfo> paymentCardData = new ArrayList<>();
        List<WalletCardViewInfo> paymentCardData = walletCards.stream().map(
        for (WalletCard card : walletCards) {
                card -> new QAWalletCardViewInfo(mContext, card)
            if (allUnknown || card.getCardType() == WalletCard.CARD_TYPE_PAYMENT) {
        ).collect(Collectors.toList());
                paymentCardData.add(new QAWalletCardViewInfo(mContext, card));
            }
        }


        // Get on main thread for UI updates.
        // Get on main thread for UI updates.
        mHandler.post(() -> {
        mHandler.post(() -> {
+34 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.wallet.util

import android.service.quickaccesswallet.WalletCard

/**
 * Filters wallet cards to only those of [WalletCard.CARD_TYPE_PAYMENT], or returns all cards if
 * they are all of [WalletCard.CARD_TYPE_UNKNOWN] (maintaining pre-U behavior). Used by the wallet
 * card carousel, quick settings tile, and lock screen.
 */
fun getPaymentCards(walletCards: List<WalletCard>): List<WalletCard> {
    val atLeastOneKnownCardType = walletCards.any { it.cardType != WalletCard.CARD_TYPE_UNKNOWN }

    return if (atLeastOneKnownCardType) {
        walletCards.filter { it.cardType == WalletCard.CARD_TYPE_PAYMENT }
    } else {
        walletCards
    }
}
+52 −1
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.data.quickaffordance
import android.graphics.drawable.Drawable
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.QuickAccessWalletClient
import android.service.quickaccesswallet.QuickAccessWalletClient
import android.service.quickaccesswallet.WalletCard
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.R
@@ -38,6 +39,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Before
@@ -91,6 +93,40 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
        job.cancel()
        job.cancel()
    }
    }


    @Test
    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
        runTest(UnconfinedTestDispatcher()) {
            setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null

            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)

            assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
            job.cancel()
        }

    @Test
    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
        runTest(UnconfinedTestDispatcher()) {
            setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null

            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)

            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
            assertThat(visibleModel.icon)
                .isEqualTo(
                    Icon.Loaded(
                        drawable = ICON,
                        contentDescription =
                            ContentDescription.Resource(
                                res = R.string.accessibility_wallet_button,
                            ),
                    )
                )
            job.cancel()
        }

    @Test
    @Test
    fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
    fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
        setUpState(isWalletFeatureAvailable = false)
        setUpState(isWalletFeatureAvailable = false)
@@ -187,6 +223,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
        isWalletServiceAvailable: Boolean = true,
        isWalletServiceAvailable: Boolean = true,
        isWalletQuerySuccessful: Boolean = true,
        isWalletQuerySuccessful: Boolean = true,
        hasSelectedCard: Boolean = true,
        hasSelectedCard: Boolean = true,
        cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
    ) {
    ) {
        val walletClient: QuickAccessWalletClient = mock()
        val walletClient: QuickAccessWalletClient = mock()
        whenever(walletClient.tileIcon).thenReturn(ICON)
        whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -202,7 +239,19 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
                if (isWalletQuerySuccessful) {
                if (isWalletQuerySuccessful) {
                    onWalletCardsRetrieved(
                    onWalletCardsRetrieved(
                        if (hasSelectedCard) {
                        if (hasSelectedCard) {
                            GetWalletCardsResponse(listOf(mock()), 0)
                            GetWalletCardsResponse(
                                listOf(
                                    WalletCard.Builder(
                                            /*cardId= */ CARD_ID,
                                            /*cardType= */ cardType,
                                            /*cardImage= */ mock(),
                                            /*contentDescription=  */ CARD_DESCRIPTION,
                                            /*pendingIntent= */ mock()
                                        )
                                        .build()
                                ),
                                0
                            )
                        } else {
                        } else {
                            GetWalletCardsResponse(emptyList(), 0)
                            GetWalletCardsResponse(emptyList(), 0)
                        }
                        }
@@ -216,5 +265,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {


    companion object {
    companion object {
        private val ICON: Drawable = mock()
        private val ICON: Drawable = mock()
        private const val CARD_ID: String = "Id"
        private const val CARD_DESCRIPTION: String = "Description"
    }
    }
}
}
Loading