From 2a2e0275cc787725606462d90f6575e8fe218cec Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Wed, 13 Oct 2021 19:56:09 +0800 Subject: [PATCH 1/4] NFC support in work profile Configure NFC payment settings based on userId. Bug: 202367033 Test: mauanl tests Change-Id: I97e275f374d34618b64188d5de185ec6c527e0fd --- .../nfc/NfcPaymentPreferenceController.java | 16 +- .../android/settings/nfc/PaymentBackend.java | 142 +++++++++++++----- .../settings/nfc/PaymentDefaultDialog.java | 27 ++-- 3 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/com/android/settings/nfc/NfcPaymentPreferenceController.java b/src/com/android/settings/nfc/NfcPaymentPreferenceController.java index 9d3673dff19..edb12dd9fba 100644 --- a/src/com/android/settings/nfc/NfcPaymentPreferenceController.java +++ b/src/com/android/settings/nfc/NfcPaymentPreferenceController.java @@ -20,6 +20,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.nfc.NfcAdapter; +import android.os.UserManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -127,7 +128,10 @@ public class NfcPaymentPreferenceController extends BasePreferenceController imp public CharSequence getSummary() { final PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp(); if (defaultApp != null) { - return defaultApp.label; + UserManager um = mContext.createContextAsUser( + defaultApp.userHandle, /*flags=*/0).getSystemService(UserManager.class); + + return defaultApp.label + " (" + um.getUserName() + ")"; } else { return mContext.getText(R.string.nfc_payment_default_not_set); } @@ -218,12 +222,15 @@ public class NfcPaymentPreferenceController extends BasePreferenceController imp } // Prevent checked callback getting called on recycled views + UserManager um = mContext.createContextAsUser( + appInfo.userHandle, /*flags=*/0).getSystemService(UserManager.class); + holder.radioButton.setOnCheckedChangeListener(null); holder.radioButton.setChecked(appInfo.isDefault); - holder.radioButton.setContentDescription(appInfo.label); + holder.radioButton.setContentDescription(appInfo.label + " (" + um.getUserName() + ")"); holder.radioButton.setOnCheckedChangeListener(this); holder.radioButton.setTag(appInfo); - holder.radioButton.setText(appInfo.label); + holder.radioButton.setText(appInfo.label + " (" + um.getUserName() + ")"); return convertView; } @@ -245,7 +252,8 @@ public class NfcPaymentPreferenceController extends BasePreferenceController imp private void makeDefault(PaymentAppInfo appInfo) { if (!appInfo.isDefault) { - mPaymentBackend.setDefaultPaymentApp(appInfo.componentName); + mPaymentBackend.setDefaultPaymentApp(appInfo.componentName, + appInfo.userHandle.getIdentifier()); } final Dialog dialog = mPreference.getDialog(); if (dialog != null) { diff --git a/src/com/android/settings/nfc/PaymentBackend.java b/src/com/android/settings/nfc/PaymentBackend.java index aec7343cbc0..542c95bb6d6 100644 --- a/src/com/android/settings/nfc/PaymentBackend.java +++ b/src/com/android/settings/nfc/PaymentBackend.java @@ -16,11 +16,10 @@ package com.android.settings.nfc; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.nfc.NfcAdapter; import android.nfc.cardemulation.ApduServiceInfo; import android.nfc.cardemulation.CardEmulation; @@ -50,6 +49,15 @@ public class PaymentBackend { boolean isDefault; public ComponentName componentName; public ComponentName settingsComponent; + public UserHandle userHandle; + } + + /** + * ComponentName of the payment application and the userId that it belongs to. + */ + public static class PaymentInfo { + public ComponentName componentName; + public int userId; } private final Context mContext; @@ -80,40 +88,55 @@ public class PaymentBackend { public void refresh() { PackageManager pm = mContext.getPackageManager(); - List serviceInfos = - mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT); - ArrayList appInfos = new ArrayList(); + ArrayList appInfosAllProfiles = new ArrayList(); - if (serviceInfos == null) { - makeCallbacks(); - return; - } + UserManager um = mContext.createContextAsUser( + UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) + .getSystemService(UserManager.class); + List userHandles = um.getEnabledProfiles(); - ComponentName defaultAppName = getDefaultPaymentApp(); + PaymentInfo defaultAppName = getDefaultPaymentApp(); PaymentAppInfo foundDefaultApp = null; - for (ApduServiceInfo service : serviceInfos) { - PaymentAppInfo appInfo = new PaymentAppInfo(); - appInfo.label = service.loadLabel(pm); - if (appInfo.label == null) { - appInfo.label = service.loadAppLabel(pm); + for (UserHandle uh : userHandles) { + List serviceInfosByProfile = + mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT, uh.getIdentifier()); + if (serviceInfosByProfile == null) continue; + + ArrayList appInfos = new ArrayList(); + + for (ApduServiceInfo service : serviceInfosByProfile) { + PaymentAppInfo appInfo = new PaymentAppInfo(); + appInfo.userHandle = uh; + appInfo.label = service.loadLabel(pm); + if (appInfo.label == null) { + appInfo.label = service.loadAppLabel(pm); + } + if (defaultAppName == null) { + appInfo.isDefault = false; + } else { + appInfo.isDefault = + service.getComponent().equals(defaultAppName.componentName) + && defaultAppName.userId == uh.getIdentifier(); + } + if (appInfo.isDefault) { + foundDefaultApp = appInfo; + } + appInfo.componentName = service.getComponent(); + String settingsActivity = service.getSettingsActivityName(); + if (settingsActivity != null) { + appInfo.settingsComponent = new ComponentName( + appInfo.componentName.getPackageName(), + settingsActivity); + } else { + appInfo.settingsComponent = null; + } + appInfo.description = service.getDescription(); + + appInfos.add(appInfo); } - appInfo.isDefault = service.getComponent().equals(defaultAppName); - if (appInfo.isDefault) { - foundDefaultApp = appInfo; - } - appInfo.componentName = service.getComponent(); - String settingsActivity = service.getSettingsActivityName(); - if (settingsActivity != null) { - appInfo.settingsComponent = new ComponentName( - appInfo.componentName.getPackageName(), - settingsActivity); - } else { - appInfo.settingsComponent = null; - } - appInfo.description = service.getDescription(); - appInfos.add(appInfo); + appInfosAllProfiles.addAll(appInfos); } - mAppInfos = appInfos; + mAppInfos = appInfosAllProfiles; mDefaultAppInfo = foundDefaultApp; makeCallbacks(); } @@ -150,13 +173,36 @@ public class PaymentBackend { } void setForegroundMode(boolean foreground) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_FOREGROUND, foreground ? 1 : 0, UserHandle.myUserId()); + UserManager um = mContext.createContextAsUser( + UserHandle.of(UserHandle.myUserId()), /*flags=*/0) + .getSystemService(UserManager.class); + List userHandles = um.getEnabledProfiles(); + for (UserHandle uh : userHandles) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_FOREGROUND, foreground ? 1 : 0, uh.getIdentifier()); + } } - ComponentName getDefaultPaymentApp() { + PaymentInfo getDefaultPaymentApp() { + UserManager um = mContext.createContextAsUser( + UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) + .getSystemService(UserManager.class); + List userHandles = um.getEnabledProfiles(); + for (UserHandle uh : userHandles) { + ComponentName defaultApp = getDefaultPaymentApp(uh.getIdentifier()); + if (defaultApp != null) { + PaymentInfo appInfo = new PaymentInfo(); + appInfo.userId = uh.getIdentifier(); + appInfo.componentName = defaultApp; + return appInfo; + } + } + return null; + } + + ComponentName getDefaultPaymentApp(int userId) { String componentString = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, UserHandle.myUserId()); + Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, userId); if (componentString != null) { return ComponentName.unflattenFromString(componentString); } else { @@ -165,9 +211,29 @@ public class PaymentBackend { } public void setDefaultPaymentApp(ComponentName app) { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, - app != null ? app.flattenToString() : null, UserHandle.myUserId()); + setDefaultPaymentApp(app, UserHandle.myUserId()); + } + + /** + * Set Nfc default payment application + */ + public void setDefaultPaymentApp(ComponentName app, int userId) { + UserManager um = mContext.createContextAsUser( + UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0) + .getSystemService(UserManager.class); + List userHandles = um.getEnabledProfiles(); + + for (UserHandle uh : userHandles) { + if (uh.getIdentifier() == userId) { + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, + app != null ? app.flattenToString() : null, uh.getIdentifier()); + } else { + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, + null, uh.getIdentifier()); + } + } refresh(); } diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java index 1aa8dca9bd1..df6d86ffb24 100644 --- a/src/com/android/settings/nfc/PaymentDefaultDialog.java +++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java @@ -21,12 +21,14 @@ import android.content.DialogInterface; import android.content.Intent; import android.nfc.cardemulation.CardEmulation; import android.os.Bundle; +import android.os.UserHandle; import android.util.Log; import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; import com.android.settings.nfc.PaymentBackend.PaymentAppInfo; +import com.android.settings.nfc.PaymentBackend.PaymentInfo; import java.util.List; @@ -37,7 +39,7 @@ public final class PaymentDefaultDialog extends AlertActivity implements private static final int PAYMENT_APP_MAX_CAPTION_LENGTH = 40; private PaymentBackend mBackend; - private ComponentName mNewDefault; + private PaymentInfo mNewDefault; @Override protected void onCreate(Bundle savedInstanceState) { @@ -51,9 +53,10 @@ public final class PaymentDefaultDialog extends AlertActivity implements ComponentName component = intent.getParcelableExtra( CardEmulation.EXTRA_SERVICE_COMPONENT); String category = intent.getStringExtra(CardEmulation.EXTRA_CATEGORY); + int userId = intent.getIntExtra(CardEmulation.EXTRA_USERID, UserHandle.myUserId()); setResult(RESULT_CANCELED); - if (!buildDialog(component, category)) { + if (!buildDialog(component, category, userId)) { finish(); } @@ -63,7 +66,7 @@ public final class PaymentDefaultDialog extends AlertActivity implements public void onClick(DialogInterface dialog, int which) { switch (which) { case BUTTON_POSITIVE: - mBackend.setDefaultPaymentApp(mNewDefault); + mBackend.setDefaultPaymentApp(mNewDefault.componentName, mNewDefault.userId); setResult(RESULT_OK); break; case BUTTON_NEGATIVE: @@ -71,7 +74,7 @@ public final class PaymentDefaultDialog extends AlertActivity implements } } - private boolean buildDialog(ComponentName component, String category) { + private boolean buildDialog(ComponentName component, String category, int userId) { if (component == null || category == null) { Log.e(TAG, "Component or category are null"); return false; @@ -88,10 +91,12 @@ public final class PaymentDefaultDialog extends AlertActivity implements List services = mBackend.getPaymentAppInfos(); for (PaymentAppInfo service : services) { - if (component.equals(service.componentName)) { + // check if userId matches + if (component.equals(service.componentName) + && service.userHandle.getIdentifier() == userId) { requestedPaymentApp = service; } - if (service.isDefault) { + if (service.isDefault && service.userHandle.getIdentifier() == userId) { defaultPaymentApp = service; } } @@ -102,13 +107,17 @@ public final class PaymentDefaultDialog extends AlertActivity implements } // Get current mode and default component - ComponentName defaultComponent = mBackend.getDefaultPaymentApp(); - if (defaultComponent != null && defaultComponent.equals(component)) { + PaymentInfo defaultComponent = mBackend.getDefaultPaymentApp(); + if (defaultComponent != null && defaultComponent.componentName.equals(component) + && defaultComponent.userId == userId) { Log.e(TAG, "Component " + component + " is already default."); return false; } - mNewDefault = component; + mNewDefault = new PaymentInfo(); + mNewDefault.componentName = component; + mNewDefault.userId = userId; + // Compose dialog; get final AlertController.AlertParams p = mAlertParams; if (defaultPaymentApp == null) { From 251e326d21846f5a916e53d181a76edc4c961e3a Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Thu, 18 Nov 2021 17:37:56 +0800 Subject: [PATCH 2/4] Redesign homepage layout for two-pane mode. In a high level, I created two versions of app bar layout. One for single pane design, another for two-pane design. Then, app initilizes two different layouts in the beginning, we simply show/hide one version while app is receiving the configuration changes update. Test: Rebuilt apk and observed the screen. Bug: 195293058 Change-Id: Icd19ea02ab1be4e964701b22ae9e20c9e00d3c0d --- res/layout/search_bar_two_pane_version.xml | 41 +++++++++ ..._homepage_app_bar_regular_phone_layout.xml | 47 ++++++++++ ...tings_homepage_app_bar_two_pane_layout.xml | 46 ++++++++++ res/layout/settings_homepage_container.xml | 29 ++---- .../homepage/SettingsHomepageActivity.java | 88 +++++++++++++++---- 5 files changed, 214 insertions(+), 37 deletions(-) create mode 100644 res/layout/search_bar_two_pane_version.xml create mode 100644 res/layout/settings_homepage_app_bar_regular_phone_layout.xml create mode 100644 res/layout/settings_homepage_app_bar_two_pane_layout.xml diff --git a/res/layout/search_bar_two_pane_version.xml b/res/layout/search_bar_two_pane_version.xml new file mode 100644 index 00000000000..a869853d4bc --- /dev/null +++ b/res/layout/search_bar_two_pane_version.xml @@ -0,0 +1,41 @@ + + + + + + + + \ No newline at end of file diff --git a/res/layout/settings_homepage_app_bar_regular_phone_layout.xml b/res/layout/settings_homepage_app_bar_regular_phone_layout.xml new file mode 100644 index 00000000000..110376b356f --- /dev/null +++ b/res/layout/settings_homepage_app_bar_regular_phone_layout.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/settings_homepage_app_bar_two_pane_layout.xml b/res/layout/settings_homepage_app_bar_two_pane_layout.xml new file mode 100644 index 00000000000..3777f612b21 --- /dev/null +++ b/res/layout/settings_homepage_app_bar_two_pane_layout.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/settings_homepage_container.xml b/res/layout/settings_homepage_container.xml index 4fd62fdfe29..a4b556dfe34 100644 --- a/res/layout/settings_homepage_container.xml +++ b/res/layout/settings_homepage_container.xml @@ -65,29 +65,14 @@ android:orientation="vertical" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"> - - - - - - - + + diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 4609cecc790..909c146627b 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -25,6 +25,7 @@ import android.app.ActivityManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.text.TextUtils; import android.util.ArraySet; @@ -77,8 +78,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements private TopLevelSettings mMainFragment; private View mHomepageView; private View mSuggestionView; + private View mTwoPaneSuggestionView; private CategoryMixin mCategoryMixin; private Set mLoadedListeners; + private boolean mIsEmbeddingActivityEnabled; + private boolean mIsTwoPaneLastTime; /** A listener receiving homepage loaded events. */ public interface HomepageLoadedListener { @@ -87,10 +91,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements } /** - * Try to add a {@link HomepageLoadedListener}. If homepage is already loaded, the listener - * will not be notified. + * Try to add a {@link HomepageLoadedListener}. If homepage is already loaded, the listener + * will not be notified. * - * @return Whether the listener is added. + * @return Whether the listener is added. */ public boolean addHomepageLoadedListener(HomepageLoadedListener listener) { if (mHomepageView == null) { @@ -113,7 +117,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements } Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion); final View homepageView = mHomepageView; - mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); + if (!mIsTwoPaneLastTime) { + mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); + } else { + mTwoPaneSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE); + } mHomepageView = null; mLoadedListeners.forEach(listener -> listener.onHomepageLoaded()); @@ -135,30 +143,25 @@ public class SettingsHomepageActivity extends FragmentActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.settings_homepage_container); + mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); + mIsTwoPaneLastTime = ActivityEmbeddingUtils.isTwoPaneResolution(this); final View appBar = findViewById(R.id.app_bar_container); appBar.setMinimumHeight(getSearchBoxHeight()); initHomepageContainer(); + updateHomepageAppBar(); mLoadedListeners = new ArraySet<>(); - final Toolbar toolbar = findViewById(R.id.search_action_bar); - FeatureFactory.getFactory(this).getSearchFeatureProvider() - .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE); + initSearchBarView(); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); mCategoryMixin = new CategoryMixin(this); getLifecycle().addObserver(mCategoryMixin); + // Only allow features on high ram devices. if (!getSystemService(ActivityManager.class).isLowRamDevice()) { - // Only allow features on high ram devices. - final ImageView avatarView = findViewById(R.id.account_avatar); - if (AvatarViewMixin.isAvatarSupported(this)) { - avatarView.setVisibility(View.VISIBLE); - getLifecycle().addObserver(new AvatarViewMixin(this, avatarView)); - } - + initAvatarView(); showSuggestionFragment(); - if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) { showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); } @@ -196,6 +199,43 @@ public class SettingsHomepageActivity extends FragmentActivity implements launchDeepLinkIntentToRight(); } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + final boolean isTwoPane = ActivityEmbeddingUtils.isTwoPaneResolution(this); + if (mIsTwoPaneLastTime != isTwoPane) { + mIsTwoPaneLastTime = isTwoPane; + updateHomepageAppBar(); + } + } + + private void initSearchBarView() { + final Toolbar toolbar = findViewById(R.id.search_action_bar); + FeatureFactory.getFactory(this).getSearchFeatureProvider() + .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE); + + if (mIsEmbeddingActivityEnabled) { + final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane); + FeatureFactory.getFactory(this).getSearchFeatureProvider() + .initSearchToolbar(this /* activity */, toolbarTwoPaneVersion, + SettingsEnums.SETTINGS_HOMEPAGE); + } + } + + private void initAvatarView() { + final ImageView avatarView = findViewById(R.id.account_avatar); + final ImageView avatarTwoPaneView = findViewById(R.id.account_avatar_two_pane_version); + if (AvatarViewMixin.isAvatarSupported(this)) { + avatarView.setVisibility(View.VISIBLE); + getLifecycle().addObserver(new AvatarViewMixin(this, avatarView)); + + if (mIsEmbeddingActivityEnabled) { + avatarTwoPaneView.setVisibility(View.VISIBLE); + getLifecycle().addObserver(new AvatarViewMixin(this, avatarTwoPaneView)); + } + } + } + private void showSuggestionFragment() { final Class fragment = FeatureFactory.getFactory(this) .getSuggestionFeatureProvider(this).getContextualSuggestionFragment(); @@ -204,6 +244,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements } mSuggestionView = findViewById(R.id.suggestion_content); + mTwoPaneSuggestionView = findViewById(R.id.two_pane_suggestion_content); mHomepageView = findViewById(R.id.settings_homepage_container); // Hide the homepage for preparing the suggestion. mHomepageView.setVisibility(View.INVISIBLE); @@ -212,6 +253,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements HOMEPAGE_LOADING_TIMEOUT_MS); try { showFragment(fragment.getConstructor().newInstance(), R.id.suggestion_content); + if (mIsEmbeddingActivityEnabled) { + showFragment(fragment.getConstructor().newInstance(), + R.id.two_pane_suggestion_content); + } } catch (Exception e) { Log.w(TAG, "Cannot show fragment", e); } @@ -332,6 +377,19 @@ public class SettingsHomepageActivity extends FragmentActivity implements view.requestFocus(); } + private void updateHomepageAppBar() { + if (!mIsEmbeddingActivityEnabled) { + return; + } + if (ActivityEmbeddingUtils.isTwoPaneResolution(this)) { + findViewById(R.id.homepage_app_bar_regular_phone_view).setVisibility(View.GONE); + findViewById(R.id.homepage_app_bar_two_pane_view).setVisibility(View.VISIBLE); + } else { + findViewById(R.id.homepage_app_bar_regular_phone_view).setVisibility(View.VISIBLE); + findViewById(R.id.homepage_app_bar_two_pane_view).setVisibility(View.GONE); + } + } + private int getSearchBoxHeight() { final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height); final int searchBarMargin = getResources().getDimensionPixelSize(R.dimen.search_bar_margin); From 7259d14324382e2ebcbf49e823370b393290db9a Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Fri, 19 Nov 2021 17:06:59 +0800 Subject: [PATCH 3/4] Remove the 'new' word for accessibility gesture tutorial dialog Root Cause: We merge back accessibility gesture feature into sc-v2, but the tutorial dialog is still the original content. Solution: Since it is not the new feature anymore, we just want to introduce the feature, so remove the 'new' word. Bug: 204850690 Test: manual test Change-Id: I38aff0cee6779153066e414e654611fa83d0a533 --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 47b5e5ac624..c730d9871dc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5334,7 +5334,7 @@ Use gesture to open - Use new accessibility gesture + Use accessibility gesture To use this feature, tap the accessibility button %s on the bottom of your screen.\n\nTo switch between features, touch & hold the accessibility button. From 73f163397360d535c5966ad6b3ad8613960e7448 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Mon, 22 Nov 2021 07:50:43 +0800 Subject: [PATCH 4/4] [MEP] eSIM deprecated API Refactoring The telephony APIs were changed, the Settings needs the refactoring. Bug: 206801604 Test: build pass. atest QuerySimSlotIndexTest (PASS) atest SelectableSubscriptionsTest (PASS) Change-Id: Ic3244333b7d3750b88ecb3dbc5d7036da50b9d4d --- .../network/helper/QuerySimSlotIndex.java | 36 +- .../network/helper/QuerySimSlotIndexTest.java | 322 ++++++++++++++++++ 2 files changed, 342 insertions(+), 16 deletions(-) create mode 100644 tests/unit/src/com/android/settings/network/helper/QuerySimSlotIndexTest.java diff --git a/src/com/android/settings/network/helper/QuerySimSlotIndex.java b/src/com/android/settings/network/helper/QuerySimSlotIndex.java index b70a148d2e4..ee53f94966f 100644 --- a/src/com/android/settings/network/helper/QuerySimSlotIndex.java +++ b/src/com/android/settings/network/helper/QuerySimSlotIndex.java @@ -17,14 +17,16 @@ package com.android.settings.network.helper; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.UiccPortInfo; import android.telephony.UiccSlotInfo; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.stream.IntStream; /** - * This is a Callable class which query slot index within device + * This is a Callable class which query logical slot index within device */ public class QuerySimSlotIndex implements Callable { private static final String TAG = "QuerySimSlotIndex"; @@ -58,30 +60,32 @@ public class QuerySimSlotIndex implements Callable { return new AtomicIntegerArray(0); } int slotIndexFilter = mOnlySlotWithSim ? 0 : SubscriptionManager.INVALID_SIM_SLOT_INDEX; + return new AtomicIntegerArray(Arrays.stream(slotInfo) - .filter(slot -> filterSlot(slot)) - .mapToInt(slot -> mapToSlotIndex(slot)) + .flatMapToInt(slot -> mapToLogicalSlotIndex(slot)) .filter(slotIndex -> (slotIndex >= slotIndexFilter)) .toArray()); } - protected boolean filterSlot(UiccSlotInfo slotInfo) { + protected IntStream mapToLogicalSlotIndex(UiccSlotInfo slotInfo) { + if (slotInfo == null) { + return IntStream.of(SubscriptionManager.INVALID_SIM_SLOT_INDEX); + } + if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ABSENT) { + return IntStream.of(SubscriptionManager.INVALID_SIM_SLOT_INDEX); + } + return slotInfo.getPorts().stream() + .filter(port -> filterPort(port)) + .mapToInt(port -> port.getLogicalSlotIndex()); + } + + protected boolean filterPort(UiccPortInfo uiccPortInfo) { if (mDisabledSlotsIncluded) { return true; } - if (slotInfo == null) { + if (uiccPortInfo == null) { return false; } - return slotInfo.getIsActive(); - } - - protected int mapToSlotIndex(UiccSlotInfo slotInfo) { - if (slotInfo == null) { - return SubscriptionManager.INVALID_SIM_SLOT_INDEX; - } - if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ABSENT) { - return SubscriptionManager.INVALID_SIM_SLOT_INDEX; - } - return slotInfo.getLogicalSlotIdx(); + return uiccPortInfo.isActive(); } } \ No newline at end of file diff --git a/tests/unit/src/com/android/settings/network/helper/QuerySimSlotIndexTest.java b/tests/unit/src/com/android/settings/network/helper/QuerySimSlotIndexTest.java new file mode 100644 index 00000000000..36532a12bdc --- /dev/null +++ b/tests/unit/src/com/android/settings/network/helper/QuerySimSlotIndexTest.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2021 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.settings.network.helper; + +import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.telephony.TelephonyManager; +import android.telephony.UiccPortInfo; +import android.telephony.UiccSlotInfo; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settingslib.utils.ThreadUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicIntegerArray; + +@RunWith(AndroidJUnit4.class) +public class QuerySimSlotIndexTest { + private static final String TAG = "QSSI_Test"; + + @Mock + private TelephonyManager mTelephonyManager; + + Future mActiveSimSlotIndex; + Future mAllSimSlotIndex; + + @Before + public void setUp() { + // query in background thread + mAllSimSlotIndex = ThreadUtils.postOnBackgroundThread( + new QuerySimSlotIndex(mTelephonyManager, true, true)); + // query in background thread + mActiveSimSlotIndex = ThreadUtils.postOnBackgroundThread( + new QuerySimSlotIndex(mTelephonyManager, false, true)); + } + + @Test + public void allSimSlotIndexCall_nullInput_getNoneNullEmptyList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(null); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(0); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_oneSimAndActivePsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSim_ActivePsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(0); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_oneSimAndActiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSim_ActiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_twoSimsAndActivePsimActiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActivePsimActiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_twoSimsAndtwoActiveEsims_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_twoActiveEsims()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_twoSimsAndActivePsimInactiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActivePsimInactiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void allSimSlotIndexCall_twoSimsAndActiveEsimInactivePsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActiveEsimInactivePsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_nullInput_getNoneNullEmptyList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(null); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(0); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_oneSimAndActivePsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSim_ActivePsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(0); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_oneSimAndActiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSim_ActiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_twoSimsAndActivePsimActiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActivePsimActiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_twoSimsAndtwoActiveEsims_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_twoActiveEsims()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(0)).isEqualTo(0); + assertThat(result.get(1)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_twoSimsAndActivePsimInactiveEsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActivePsimInactiveEsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(0); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + @Test + public void activeSimSlotIndexCall_twoSimsAndActiveEsimInactivePsim_getList() { + try { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(twoSims_ActiveEsimInactivePsim()); + List result = SelectableSubscriptions.atomicToList(mActiveSimSlotIndex.get()); + + assertThat(result.size()).isEqualTo(1); + assertThat(result.get(0)).isEqualTo(1); + } catch (Exception exception) { + Log.w(TAG, "Fail to request subIdList", exception); + } + } + + private UiccSlotInfo[] oneSim_ActivePsim() { + return new UiccSlotInfo[]{createUiccSlotInfo(false, 0, true)}; + } + + private UiccSlotInfo[] oneSim_ActiveEsim() { + return new UiccSlotInfo[]{createUiccSlotInfo(true, 1, true)}; + } + + private UiccSlotInfo[] twoSims_ActivePsimActiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, 0, true), + createUiccSlotInfo(true, 1, true)}; + } + + private UiccSlotInfo[] twoSims_twoActiveEsims() { + return new UiccSlotInfo[]{ + createUiccSlotInfoForTwoEsim(true, true)}; + } + + private UiccSlotInfo[] twoSims_ActivePsimInactiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, 0, true), + createUiccSlotInfo(true, 1, false)}; + } + + private UiccSlotInfo[] twoSims_ActiveEsimInactivePsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, 0, false), + createUiccSlotInfo(true, 1, true)}; + } + + //ToDo: add more cases. + /* + private List threeSims_ActivePsimTwoinactiveEsim(){ + } + private List threeSims_twoActiveEsimsInactivePsim(){ + } + private List threeSims_ActiveEsimInactivePsimInactiveEsim(){ + } + private List threeSims_ActivePsimActiveEsimInactiveEsim(){ + } + */ + + private UiccSlotInfo createUiccSlotInfo(boolean isEuicc, int logicalSlotIdx, + boolean isActive) { + return new UiccSlotInfo( + isEuicc, /* isEuicc */ + "123", /* cardId */ + CARD_STATE_INFO_PRESENT, /* cardStateInfo */ + true, /* isExtendApduSupported */ + true, /* isRemovable */ + Collections.singletonList( + new UiccPortInfo("" /* iccId */, 0 /* portIdx */, + logicalSlotIdx /* logicalSlotIdx */, isActive /* isActive */)) + ); + } + + private UiccSlotInfo createUiccSlotInfoForTwoEsim(boolean isActiveEsim1, + boolean isActiveEsim2) { + return new UiccSlotInfo( + true, /* isEuicc */ + "123", /* cardId */ + CARD_STATE_INFO_PRESENT, /* cardStateInfo */ + true, /* isExtendApduSupported */ + true, /* isRemovable */ + Arrays.asList( + new UiccPortInfo("" /* iccId */, 0 /* portIdx */, + 0 /* logicalSlotIdx */, isActiveEsim1 /* isActive */), + new UiccPortInfo("" /* iccId */, 1 /* portIdx */, + 1 /* logicalSlotIdx */, isActiveEsim2 /* isActive */)) + ); + } +}