Snap for 7938361 from a88ab65024 to tm-release

Change-Id: Id125601d3c27df75abd0a38ce0df17d0d835c8ae
This commit is contained in:
Android Build Coastguard Worker
2021-11-23 02:08:37 +00:00
11 changed files with 691 additions and 105 deletions

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/SearchBarStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="@dimen/search_bar_margin">
<Toolbar
android:id="@+id/search_action_bar_two_pane"
android:layout_width="match_parent"
android:layout_height="@dimen/search_bar_height"
android:paddingStart="4dp"
android:background="@drawable/search_bar_selected_background"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
<TextView
style="@style/TextAppearance.SearchBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="-4dp"
android:layout_gravity="start"
android:text="@string/search_menu"/>
</Toolbar>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/account_avatar"
android:layout_width="@dimen/avatar_length"
android:layout_height="@dimen/avatar_length"
android:layout_marginTop="@dimen/avatar_margin_top"
android:layout_marginEnd="@dimen/avatar_margin_end"
android:layout_gravity="end"
android:visibility="invisible"
android:accessibilityTraversalAfter="@id/homepage_title"
android:contentDescription="@string/search_bar_account_avatar_content_description"/>
<TextView
android:id="@+id/homepage_title"
android:text="@string/settings_label"
style="@style/HomepageTitleText"/>
<FrameLayout
android:id="@+id/suggestion_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<include layout="@layout/search_bar"/>
</LinearLayout>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/two_pane_suggestion_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:orientation="horizontal">
<include layout="@layout/search_bar_two_pane_version"/>
<ImageView
android:id="@+id/account_avatar_two_pane_version"
android:layout_width="@dimen/avatar_length"
android:layout_height="@dimen/avatar_length"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:contentDescription="@string/search_bar_account_avatar_content_description"/>
</LinearLayout>
</LinearLayout>

View File

@@ -65,29 +65,14 @@
android:orientation="vertical"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
<ImageView
android:id="@+id/account_avatar"
android:layout_width="@dimen/avatar_length"
android:layout_height="@dimen/avatar_length"
android:layout_marginTop="@dimen/avatar_margin_top"
android:layout_marginEnd="@dimen/avatar_margin_end"
android:layout_gravity="end"
android:visibility="invisible"
android:accessibilityTraversalAfter="@id/homepage_title"
android:contentDescription="@string/search_bar_account_avatar_content_description"/>
<TextView
android:id="@+id/homepage_title"
android:text="@string/settings_label"
style="@style/HomepageTitleText"/>
<FrameLayout
android:id="@+id/suggestion_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<include layout="@layout/search_bar"/>
<include
android:id="@+id/homepage_app_bar_regular_phone_view"
layout="@layout/settings_homepage_app_bar_regular_phone_layout"/>
<include
android:id="@+id/homepage_app_bar_two_pane_view"
layout="@layout/settings_homepage_app_bar_two_pane_layout"
android:visibility="gone"/>
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -5344,7 +5344,7 @@
<!-- Title for the accessibility tutorial dialog in accessibility service with gesture. [CHAR LIMIT=50] -->
<string name="accessibility_tutorial_dialog_title_gesture">Use gesture to open</string>
<!-- Title for the accessibility tutorial dialog in gesture navigation settings. [CHAR LIMIT=50] -->
<string name="accessibility_tutorial_dialog_title_gesture_settings">Use new accessibility gesture</string>
<string name="accessibility_tutorial_dialog_title_gesture_settings">Use accessibility gesture</string>
<!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the 3-button nav bar. [CHAR LIMIT=NONE] -->
<string name="accessibility_tutorial_dialog_message_button">To use this feature, tap the accessibility button <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> on the bottom of your screen.\n\nTo switch between features, touch &amp; hold the accessibility button.</string>
<!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the accessibility floating button. [CHAR LIMIT=100] -->

View File

@@ -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<HomepageLoadedListener> mLoadedListeners;
private boolean mIsEmbeddingActivityEnabled;
private boolean mIsTwoPaneLastTime;
/** A listener receiving homepage loaded events. */
public interface HomepageLoadedListener {
@@ -113,7 +117,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements
}
Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion);
final View homepageView = mHomepageView;
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);
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));
}
if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
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<? extends Fragment> 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);

View File

@@ -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<AtomicIntegerArray> {
private static final String TAG = "QuerySimSlotIndex";
@@ -58,30 +60,32 @@ public class QuerySimSlotIndex implements Callable<AtomicIntegerArray> {
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();
}
}

View File

@@ -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) {

View File

@@ -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,24 +88,36 @@ public class PaymentBackend {
public void refresh() {
PackageManager pm = mContext.getPackageManager();
List<ApduServiceInfo> serviceInfos =
mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT);
ArrayList<PaymentAppInfo> appInfosAllProfiles = new ArrayList<PaymentAppInfo>();
UserManager um = mContext.createContextAsUser(
UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
.getSystemService(UserManager.class);
List<UserHandle> userHandles = um.getEnabledProfiles();
PaymentInfo defaultAppName = getDefaultPaymentApp();
PaymentAppInfo foundDefaultApp = null;
for (UserHandle uh : userHandles) {
List<ApduServiceInfo> serviceInfosByProfile =
mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT, uh.getIdentifier());
if (serviceInfosByProfile == null) continue;
ArrayList<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
if (serviceInfos == null) {
makeCallbacks();
return;
}
ComponentName defaultAppName = getDefaultPaymentApp();
PaymentAppInfo foundDefaultApp = null;
for (ApduServiceInfo service : serviceInfos) {
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);
}
appInfo.isDefault = service.getComponent().equals(defaultAppName);
if (defaultAppName == null) {
appInfo.isDefault = false;
} else {
appInfo.isDefault =
service.getComponent().equals(defaultAppName.componentName)
&& defaultAppName.userId == uh.getIdentifier();
}
if (appInfo.isDefault) {
foundDefaultApp = appInfo;
}
@@ -111,9 +131,12 @@ public class PaymentBackend {
appInfo.settingsComponent = null;
}
appInfo.description = service.getDescription();
appInfos.add(appInfo);
}
mAppInfos = appInfos;
appInfosAllProfiles.addAll(appInfos);
}
mAppInfos = appInfosAllProfiles;
mDefaultAppInfo = foundDefaultApp;
makeCallbacks();
}
@@ -150,13 +173,36 @@ public class PaymentBackend {
}
void setForegroundMode(boolean foreground) {
UserManager um = mContext.createContextAsUser(
UserHandle.of(UserHandle.myUserId()), /*flags=*/0)
.getSystemService(UserManager.class);
List<UserHandle> userHandles = um.getEnabledProfiles();
for (UserHandle uh : userHandles) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_FOREGROUND, foreground ? 1 : 0, UserHandle.myUserId());
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<UserHandle> 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) {
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<UserHandle> 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, UserHandle.myUserId());
app != null ? app.flattenToString() : null, uh.getIdentifier());
} else {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
null, uh.getIdentifier());
}
}
refresh();
}

View File

@@ -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;
@@ -39,7 +41,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) {
@@ -56,9 +58,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();
}
@@ -68,7 +71,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:
@@ -76,7 +79,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;
@@ -93,10 +96,12 @@ public final class PaymentDefaultDialog extends AlertActivity implements
List<PaymentAppInfo> 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;
}
}
@@ -107,13 +112,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) {

View File

@@ -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<AtomicIntegerArray> mActiveSimSlotIndex;
Future<AtomicIntegerArray> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<UiccSlotInfo> threeSims_ActivePsimTwoinactiveEsim(){
}
private List<UiccSlotInfo> threeSims_twoActiveEsimsInactivePsim(){
}
private List<UiccSlotInfo> threeSims_ActiveEsimInactivePsimInactiveEsim(){
}
private List<UiccSlotInfo> 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 */))
);
}
}