From cfb4ca74c149938eab64a29bd5636c1e02a83f9d Mon Sep 17 00:00:00 2001 From: Yong Shi Date: Tue, 28 Aug 2018 16:59:15 +0900 Subject: [PATCH 01/11] Show the disclaimer regarding WFC location privacy policy In some countries the rules is very strict about responsibility and liability around location data during emergency calls. The purpose of this feature is to notify end user that the location information will be shared for calls over wifi calling. Test: manual - Checked that WFC locatopn privacy policy is shown when changing wifi calling setting to turned on. Test: auto - Passed LocationPolicyDisclaimerTest. Bug: 67872298 Change-Id: I03895743fb8da95269069ad7eda9a7b3e282857e Merged-In: I03895743fb8da95269069ad7eda9a7b3e282857e --- res/values/strings.xml | 6 + .../wifi/calling/DisclaimerItemFactory.java | 1 + .../calling/LocationPolicyDisclaimer.java | 90 ++++++++++++ .../calling/LocationPolicyDisclaimerTest.java | 133 ++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 src/com/android/settings/wifi/calling/LocationPolicyDisclaimer.java create mode 100644 tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index f9af39a5213..2e219423d41 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10089,4 +10089,10 @@ NO THANKS + + + Location + + + Your service provider may collect your location in order to provide this service.\n\nPlease review your service provider\u2019s privacy policy. diff --git a/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java b/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java index 6d8dc8fc177..e3994aa0e44 100644 --- a/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java +++ b/src/com/android/settings/wifi/calling/DisclaimerItemFactory.java @@ -51,6 +51,7 @@ public final class DisclaimerItemFactory { private static List getDisclaimerItemList(Context context, int subId) { List itemList = new ArrayList(); + itemList.add(new LocationPolicyDisclaimer(context, subId)); return itemList; } diff --git a/src/com/android/settings/wifi/calling/LocationPolicyDisclaimer.java b/src/com/android/settings/wifi/calling/LocationPolicyDisclaimer.java new file mode 100644 index 00000000000..6354bdd59f6 --- /dev/null +++ b/src/com/android/settings/wifi/calling/LocationPolicyDisclaimer.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 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.wifi.calling; + +import android.content.Context; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; + +/** + * Disclaimer item class for displaying location privacy policy UI on + * {@link WifiCallingDisclaimerFragment}. + */ +class LocationPolicyDisclaimer extends DisclaimerItem { + private static final String DISCLAIMER_ITEM_NAME = "LocationPolicyDisclaimer"; + @VisibleForTesting + static final String KEY_HAS_AGREED_LOCATION_DISCLAIMER + = "key_has_agreed_location_disclaimer"; + + LocationPolicyDisclaimer(Context context, int subId) { + super(context, subId); + } + + /** + * {@inheritDoc} + */ + @Override + boolean shouldShow() { + PersistableBundle config = getCarrierConfig(); + if (!config.getBoolean(CarrierConfigManager.KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL)) { + logd("shouldShow: false due to carrier config is false."); + return false; + } + + if (config.getBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL)) { + logd("shouldShow: false due to WFC is on as default."); + return false; + } + + return super.shouldShow(); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getName() { + return DISCLAIMER_ITEM_NAME; + } + + /** + * {@inheritDoc} + */ + @Override + protected int getTitleId() { + return R.string.wfc_disclaimer_location_title_text; + } + + /** + * {@inheritDoc} + */ + @Override + protected int getMessageId() { + return R.string.wfc_disclaimer_location_desc_text; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getPrefKey() { + return KEY_HAS_AGREED_LOCATION_DISCLAIMER; + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java b/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java new file mode 100644 index 00000000000..94942883e8b --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 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.wifi.calling; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class LocationPolicyDisclaimerTest { + private static final String TEST_SHARED_PREFERENCE = "test_wfc_disclaimer_prefs"; + private static final int TEST_SUB_ID = 0; + + @Mock + private CarrierConfigManager mCarrierConfigManager; + + private final PersistableBundle mBundle = new PersistableBundle(); + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + + doReturn(mCarrierConfigManager).when(mContext).getSystemService( + Context.CARRIER_CONFIG_SERVICE); + when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle); + doReturn(getSharedPreferences()).when(mContext).getSharedPreferences(anyString(), anyInt()); + } + + @Test + public void sholdShow_configTrue_shouldShowLocationPolicyDisclaimer() { + LocationPolicyDisclaimer disclaimerItem + = spy(new LocationPolicyDisclaimer(mContext, TEST_SUB_ID)); + mBundle.putBoolean(CarrierConfigManager.KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true); + mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false); + getSharedPreferences().edit().putBoolean( + LocationPolicyDisclaimer.KEY_HAS_AGREED_LOCATION_DISCLAIMER + TEST_SUB_ID, + false).commit(); + + // Check the WFC disclaimer item is should be shown. + assertThat(disclaimerItem.shouldShow()).isTrue(); + } + + @Test + public void sholdShow_configFalse_shouldNotShowLocationPolicyDisclaimer() { + LocationPolicyDisclaimer disclaimerItem = new LocationPolicyDisclaimer(mContext, + TEST_SUB_ID); + mBundle.putBoolean(CarrierConfigManager.KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false); + + // Check the WFC disclaimer item is should not be shown due to the + // KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL on carrier config is false. + assertThat(disclaimerItem.shouldShow()).isFalse(); + } + + @Test + public void sholdShow_defaultWfcEnabled_shouldNotShowLocationPolicyDisclaimer() { + LocationPolicyDisclaimer disclaimerItem + = new LocationPolicyDisclaimer(mContext, TEST_SUB_ID); + mBundle.putBoolean(CarrierConfigManager.KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true); + mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, true); + + // Check the WFC disclaimer item is should not be shown due to the + // KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL on carrier config is true. + assertThat(disclaimerItem.shouldShow()).isFalse(); + } + + @Test + public void sholdShow_alreadyAgreed_shouldNotShowLocationPolicyDisclaimer() { + LocationPolicyDisclaimer disclaimerItem = + spy(new LocationPolicyDisclaimer(mContext, TEST_SUB_ID)); + mBundle.putBoolean(CarrierConfigManager.KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true); + mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false); + getSharedPreferences().edit().putBoolean( + LocationPolicyDisclaimer.KEY_HAS_AGREED_LOCATION_DISCLAIMER + TEST_SUB_ID, true) + .commit(); + + // Check the WFC disclaimer item is should not be shown due to an item is already agreed. + assertThat(disclaimerItem.shouldShow()).isFalse(); + } + + @Test + public void onAgreed_shouldSetSharedPreferencesToAgreed() { + LocationPolicyDisclaimer disclaimerItem + =spy(new LocationPolicyDisclaimer(mContext, TEST_SUB_ID)); + + disclaimerItem.onAgreed(); + + // Check the SharedPreferences key is changed to agreed. + assertThat(getSharedPreferences().getBoolean( + LocationPolicyDisclaimer.KEY_HAS_AGREED_LOCATION_DISCLAIMER + TEST_SUB_ID, + false)).isTrue(); + } + + private SharedPreferences getSharedPreferences() { + return mContext.getSharedPreferences(TEST_SHARED_PREFERENCE, Context.MODE_PRIVATE); + } +} From 1859a19302cd714b5dbcb83e8bbe31dcef30419c Mon Sep 17 00:00:00 2001 From: Quang Luong Date: Wed, 27 Mar 2019 12:49:42 -0700 Subject: [PATCH 02/11] Added connect listener to startOsuProvisioning() Updated AccessPoint.startOsuProvisioning() call to accept a connect listener to display a toast on failure to connect. Bug: 123697580 Test: build Change-Id: I0f29b2f5ccc8f2d4b8137639725dca1bcb106b26 --- src/com/android/settings/wifi/WifiSettings.java | 2 +- src/com/android/settings/wifi/slice/ConnectToWifiHandler.java | 2 +- .../android/settings/wifi/slice/ConnectToWifiHandlerTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 91672104e45..8e7b53a8388 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -564,7 +564,7 @@ public class WifiSettings extends RestrictedSettingsFragment */ switch (WifiUtils.getConnectingType(mSelectedAccessPoint)) { case WifiUtils.CONNECT_TYPE_OSU_PROVISION: - mSelectedAccessPoint.startOsuProvisioning(); + mSelectedAccessPoint.startOsuProvisioning(mConnectListener); mClickedConnect = true; break; diff --git a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java index 7b21b65a8e0..064037e2e5d 100644 --- a/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java +++ b/src/com/android/settings/wifi/slice/ConnectToWifiHandler.java @@ -48,7 +48,7 @@ public class ConnectToWifiHandler extends Activity { void connect(AccessPoint accessPoint) { switch (WifiUtils.getConnectingType(accessPoint)) { case WifiUtils.CONNECT_TYPE_OSU_PROVISION: - accessPoint.startOsuProvisioning(); + accessPoint.startOsuProvisioning(null /* listener */); break; case WifiUtils.CONNECT_TYPE_OPEN_NETWORK: diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java index a7cf327a245..24b0b380434 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/ConnectToWifiHandlerTest.java @@ -78,7 +78,7 @@ public class ConnectToWifiHandlerTest { mHandler.connect(mAccessPoint); - verify(mAccessPoint).startOsuProvisioning(); + verify(mAccessPoint).startOsuProvisioning(null /* listener */); } From 7539cf8ed8951be2f3ef1b59efde76ac4c3d30d3 Mon Sep 17 00:00:00 2001 From: pastychang Date: Fri, 29 Mar 2019 11:17:04 +0800 Subject: [PATCH 03/11] Change accessibility setup page to use glif v3 theme Bug: 126065441 Test: Manual Change-Id: I383ac07aa588db4604323de54a37fa03078dee57 --- AndroidManifest.xml | 2 +- .../AccessibilitySettingsForSetupWizardActivityTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 51f668172ca..f696e4b678c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1394,7 +1394,7 @@ + android:theme="@style/GlifV3Theme.Light"> diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java index d6b12c35a32..a0a9de9c88a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java @@ -39,6 +39,6 @@ public class AccessibilitySettingsForSetupWizardActivityTest { AccessibilitySettingsForSetupWizardActivity activity = Robolectric.buildActivity(AccessibilitySettingsForSetupWizardActivity.class, intent).get(); - assertThat(activity.getThemeResId()).isEqualTo(R.style.GlifTheme_Light); + assertThat(activity.getThemeResId()).isEqualTo(R.style.GlifV3Theme_Light); } } From dbcdb95daa62fb91d12458c992895f9af11f28a6 Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Thu, 28 Mar 2019 21:22:35 +0800 Subject: [PATCH 04/11] Implement slices api of SettingsSearchIndexablesProvider add querySliceUriPairs method to provider Fixes: 129322803 Test: manual and robotests Change-Id: I9255ed6dba5b8b1fc79caa3026c8b31924c95f4b --- .../SettingsSearchIndexablesProvider.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java index 0a846de317f..b5982243e2c 100644 --- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java +++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java @@ -41,21 +41,28 @@ import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS; import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS; import static android.provider.SearchIndexablesContract.SITE_MAP_COLUMNS; +import static android.provider.SearchIndexablesContract.SLICE_URI_PAIRS_COLUMNS; import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP; +import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; +import android.net.Uri; import android.provider.SearchIndexableResource; import android.provider.SearchIndexablesContract; import android.provider.SearchIndexablesProvider; +import android.provider.SettingsSlicesContract; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import androidx.slice.SliceViewManager; + import com.android.settings.SettingsActivity; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.slices.SettingsSliceProvider; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.Tile; @@ -184,6 +191,33 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider { return cursor; } + @Override + public Cursor querySliceUriPairs() { + final SliceViewManager manager = SliceViewManager.getInstance(getContext()); + final MatrixCursor cursor = new MatrixCursor(SLICE_URI_PAIRS_COLUMNS); + final Uri baseUri = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .build(); + final Uri platformBaseUri = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSlicesContract.AUTHORITY) + .build(); + + final Collection sliceUris = manager.getSliceDescendants(baseUri); + sliceUris.addAll(manager.getSliceDescendants(platformBaseUri)); + + for (Uri uri : sliceUris) { + cursor.newRow() + .add(SearchIndexablesContract.SliceUriPairColumns.KEY, uri.getLastPathSegment()) + .add(SearchIndexablesContract.SliceUriPairColumns.SLICE_URI, uri); + } + + return cursor; + } + private List getNonIndexableKeysFromProvider(Context context) { final Collection values = FeatureFactory.getFactory(context) .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues(); @@ -207,7 +241,7 @@ public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider { if (System.getProperty(SYSPROP_CRASH_ON_ERROR) != null) { throw new RuntimeException(e); } - Log.e(TAG, "Error trying to get non-indexable keys from: " + clazz.getName() , e); + Log.e(TAG, "Error trying to get non-indexable keys from: " + clazz.getName(), e); continue; } From 5cc16b2cc0dfc1730ac733d1aaa55affa5958a0f Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Thu, 14 Mar 2019 20:34:50 -0700 Subject: [PATCH 05/11] Move SIM select logic from SimSelectNotification to Telephony. SimSelectNotification used to listen to SIM_STATE_CHANGED from Telephony and do checkings to decide whether to pop up notification and SimDialogActivity. The logic needs updated and only Telephony knows it well. So this change moves the logic into Telephony. SimSelectNotification only listens to Telephony's decision, brings up notification and trigger SimDialogActivity. Bug: 128645056 Test: sanity and manual Change-Id: I6153e27dd00dd9cdf8682f135eb39f8af3a75608 Merged-In: I6153e27dd00dd9cdf8682f135eb39f8af3a75608 --- AndroidManifest.xml | 2 +- .../settings/sim/SimSelectNotification.java | 78 ++++--------------- 2 files changed, 16 insertions(+), 64 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0271c92c326..0a0e4034191 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2812,7 +2812,7 @@ - + diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java index ebf34e5ca7c..891d1719e66 100644 --- a/src/com/android/settings/sim/SimSelectNotification.java +++ b/src/com/android/settings/sim/SimSelectNotification.java @@ -16,6 +16,12 @@ package com.android.settings.sim; +import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_ID; +import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES; +import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE; +import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; +import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; + import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; @@ -23,19 +29,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.provider.Settings; -import androidx.core.app.NotificationCompat; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; -import com.android.internal.telephony.IccCardConstants; import com.android.settings.R; import com.android.settings.Settings.SimSettingsActivity; -import com.android.settings.Utils; -import java.util.List; +import androidx.core.app.NotificationCompat; public class SimSelectNotification extends BroadcastReceiver { private static final String TAG = "SimSelectNotification"; @@ -46,71 +45,24 @@ public class SimSelectNotification extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - final TelephonyManager telephonyManager = (TelephonyManager) - context.getSystemService(Context.TELEPHONY_SERVICE); - final SubscriptionManager subscriptionManager = SubscriptionManager.from(context); - final int numSlots = telephonyManager.getSimCount(); - - // Do not create notifications on single SIM devices or when provisioning i.e. Setup Wizard. - if (numSlots < 2 || !Utils.isDeviceProvisioned(context)) { - return; - } - // Cancel any previous notifications cancelNotification(context); - - // If sim state is not ABSENT or LOADED then ignore - String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); - if (!(IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus) || - IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus))) { - Log.d(TAG, "sim state is not Absent or Loaded"); - return; - } else { - Log.d(TAG, "simstatus = " + simStatus); - } - - int state; - for (int i = 0; i < numSlots; i++) { - state = telephonyManager.getSimState(i); - if (!(state == TelephonyManager.SIM_STATE_ABSENT - || state == TelephonyManager.SIM_STATE_READY - || state == TelephonyManager.SIM_STATE_UNKNOWN)) { - Log.d(TAG, "All sims not in valid state yet"); - return; - } - } - - List sil = subscriptionManager.getActiveSubscriptionInfoList(); - if (sil == null || sil.size() < 1) { - Log.d(TAG, "Subscription list is empty"); - return; - } - - // Clear defaults for any subscriptions which no longer exist - subscriptionManager.clearDefaultsForInactiveSubIds(); - - boolean dataSelected = SubscriptionManager.isUsableSubIdValue( - SubscriptionManager.getDefaultDataSubscriptionId()); - boolean smsSelected = SubscriptionManager.isUsableSubIdValue( - SubscriptionManager.getDefaultSmsSubscriptionId()); - - // If data and sms defaults are selected, dont show notification (Calls default is optional) - if (dataSelected && smsSelected) { - Log.d(TAG, "Data & SMS default sims are selected. No notification"); - return; - } - // Create a notification to tell the user that some defaults are missing createNotification(context); - if (sil.size() == 1) { + int dialogType = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE); + if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES) { + int subId = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_ID, + SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + int slotIndex = SubscriptionManager.getSlotIndex(subId); // If there is only one subscription, ask if user wants to use if for everything Intent newIntent = new Intent(context, SimDialogActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); newIntent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.PREFERRED_PICK); - newIntent.putExtra(SimDialogActivity.PREFERRED_SIM, sil.get(0).getSimSlotIndex()); + newIntent.putExtra(SimDialogActivity.PREFERRED_SIM, slotIndex); context.startActivity(newIntent); - } else if (!dataSelected) { + } else if (dialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) { // If there are mulitple, ensure they pick default data Intent newIntent = new Intent(context, SimDialogActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); From 428ba7cde651273696021d4a7269351595304023 Mon Sep 17 00:00:00 2001 From: Torbjorn Eklund Date: Thu, 28 Mar 2019 13:18:47 +0100 Subject: [PATCH 06/11] WifiCallingSettingsForSubTest: Solve casting problem in setup Solves casting problem in WifiCallingSettingsForSubTest that causes all tests in this file to fail in the setup-function. The wfc mode buttons has changed from being a ListPreference to be a ListWithEntrySummaryPreference. The test cases have now been updated accordingly. Problem was introduced by the following two patches that conflict with each other. The test cases in WifiCallingSettingsForSubTest works correctly when each patch is tested in isolation, but failed when they were both merged: 73ffcf49990ab3134b139607c0225f6ae1646c18 8498436a9613c806d4e1085074aea22f1a78675a Bug: 129545431 Test: make RunSettingsRoboTests \ ROBOTEST_FILTER=WifiCallingSettingsForSubTest Change-Id: Ib6988e514bbad2023ba6cfc2e1d8734b68cabc31 --- .../settings/wifi/calling/WifiCallingSettingsForSubTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java index fbdc30361c1..8f01d39e73e 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java @@ -92,8 +92,8 @@ public class WifiCallingSettingsForSubTest { @Mock private ToggleSwitch mToggleSwitch; @Mock private View mView; @Mock private ImsConfig mImsConfig; - @Mock private ListPreference mButtonWfcMode; - @Mock private ListPreference mButtonWfcRoamingMode; + @Mock private ListWithEntrySummaryPreference mButtonWfcMode; + @Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode; @Mock private Preference mUpdateAddress; @Before From 53d147dd5c1fa20ad87019590e6babd036e5f64f Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Mon, 1 Apr 2019 15:54:08 +0800 Subject: [PATCH 07/11] Refactor WifiScanWorker Extracted WifiScanWorker from WifiSlice Bug: 128056349 Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.wifi Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.slices Change-Id: I9b3c809ee6c2b7466c959631840b257b91b49d88 --- .../settings/wifi/slice/WifiScanWorker.java | 127 ++++++++++++++++++ .../settings/wifi/slice/WifiSlice.java | 95 ------------- .../slices/SettingsSliceProviderTest.java | 4 +- .../wifi/slice/WifiScanWorkerTest.java | 119 ++++++++++++++++ .../settings/wifi/slice/WifiSliceTest.java | 75 ++--------- 5 files changed, 257 insertions(+), 163 deletions(-) create mode 100644 src/com/android/settings/wifi/slice/WifiScanWorker.java create mode 100644 tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java new file mode 100644 index 00000000000..cf45d082f35 --- /dev/null +++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 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.wifi.slice; + +import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT; + +import android.content.Context; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Bundle; + +import com.android.settings.slices.SliceBackgroundWorker; +import com.android.settingslib.wifi.AccessPoint; +import com.android.settingslib.wifi.WifiTracker; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link SliceBackgroundWorker} for Wi-Fi, used by WifiSlice. + */ +public class WifiScanWorker extends SliceBackgroundWorker + implements WifiTracker.WifiListener { + + private final Context mContext; + + private WifiTracker mWifiTracker; + + public WifiScanWorker(Context context, Uri uri) { + super(context, uri); + mContext = context; + } + + @Override + protected void onSlicePinned() { + if (mWifiTracker == null) { + mWifiTracker = new WifiTracker(mContext, this /* wifiListener */, + true /* includeSaved */, true /* includeScans */); + } + mWifiTracker.onStart(); + onAccessPointsChanged(); + } + + @Override + protected void onSliceUnpinned() { + mWifiTracker.onStop(); + } + + @Override + public void close() { + mWifiTracker.onDestroy(); + } + + @Override + public void onWifiStateChanged(int state) { + notifySliceChange(); + } + + @Override + public void onConnectedChanged() { + } + + @Override + public void onAccessPointsChanged() { + // in case state has changed + if (!mWifiTracker.getManager().isWifiEnabled()) { + updateResults(null); + return; + } + // AccessPoints are sorted by the WifiTracker + final List accessPoints = mWifiTracker.getAccessPoints(); + final List resultList = new ArrayList<>(); + for (AccessPoint ap : accessPoints) { + if (ap.isReachable()) { + resultList.add(clone(ap)); + if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) { + break; + } + } + } + updateResults(resultList); + } + + private AccessPoint clone(AccessPoint accessPoint) { + final Bundle savedState = new Bundle(); + accessPoint.saveWifiState(savedState); + return new AccessPoint(mContext, savedState); + } + + @Override + protected boolean areListsTheSame(List a, List b) { + if (!a.equals(b)) { + return false; + } + + // compare access point states one by one + final int listSize = a.size(); + for (int i = 0; i < listSize; i++) { + if (getState(a.get(i)) != getState(b.get(i))) { + return false; + } + } + return true; + } + + private NetworkInfo.State getState(AccessPoint accessPoint) { + final NetworkInfo networkInfo = accessPoint.getNetworkInfo(); + if (networkInfo != null) { + return networkInfo.getState(); + } + return null; + } +} \ No newline at end of file diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index f2c919b1733..d3df5fccb53 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -62,9 +62,7 @@ import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiUtils; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settingslib.wifi.AccessPoint; -import com.android.settingslib.wifi.WifiTracker; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -362,97 +360,4 @@ public class WifiSlice implements CustomSliceable { public Class getBackgroundWorkerClass() { return WifiScanWorker.class; } - - public static class WifiScanWorker extends SliceBackgroundWorker - implements WifiTracker.WifiListener { - - private final Context mContext; - - private WifiTracker mWifiTracker; - - public WifiScanWorker(Context context, Uri uri) { - super(context, uri); - mContext = context; - } - - @Override - protected void onSlicePinned() { - if (mWifiTracker == null) { - mWifiTracker = new WifiTracker(mContext, this /* wifiListener */, - true /* includeSaved */, true /* includeScans */); - } - mWifiTracker.onStart(); - onAccessPointsChanged(); - } - - @Override - protected void onSliceUnpinned() { - mWifiTracker.onStop(); - } - - @Override - public void close() { - mWifiTracker.onDestroy(); - } - - @Override - public void onWifiStateChanged(int state) { - notifySliceChange(); - } - - @Override - public void onConnectedChanged() { - } - - @Override - public void onAccessPointsChanged() { - // in case state has changed - if (!mWifiTracker.getManager().isWifiEnabled()) { - updateResults(null); - return; - } - // AccessPoints are sorted by the WifiTracker - final List accessPoints = mWifiTracker.getAccessPoints(); - final List resultList = new ArrayList<>(); - for (AccessPoint ap : accessPoints) { - if (ap.isReachable()) { - resultList.add(clone(ap)); - if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) { - break; - } - } - } - updateResults(resultList); - } - - private AccessPoint clone(AccessPoint accessPoint) { - final Bundle savedState = new Bundle(); - accessPoint.saveWifiState(savedState); - return new AccessPoint(mContext, savedState); - } - - @Override - protected boolean areListsTheSame(List a, List b) { - if (!a.equals(b)) { - return false; - } - - // compare access point states one by one - final int listSize = a.size(); - for (int i = 0; i < listSize; i++) { - if (getState(a.get(i)) != getState(b.get(i))) { - return false; - } - } - return true; - } - - private State getState(AccessPoint accessPoint) { - final NetworkInfo networkInfo = accessPoint.getNetworkInfo(); - if (networkInfo != null) { - return networkInfo.getState(); - } - return null; - } - } } diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index 005ffbebc08..9f121306b8c 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -55,7 +55,7 @@ import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUtils; -import com.android.settings.wifi.slice.WifiSlice; +import com.android.settings.wifi.slice.WifiScanWorker; import com.android.settingslib.wifi.WifiTracker; import org.junit.After; @@ -474,7 +474,7 @@ public class SettingsSliceProviderTest { mProvider.onSlicePinned(uri); } - @Implements(WifiSlice.WifiScanWorker.class) + @Implements(WifiScanWorker.class) public static class ShadowWifiScanWorker { private static WifiTracker mWifiTracker; diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java new file mode 100644 index 00000000000..7ddbce48a9f --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 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.wifi.slice; + +import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.NetworkInfo; +import android.net.NetworkInfo.State; +import android.net.wifi.WifiManager; +import android.os.Bundle; + +import androidx.slice.SliceProvider; +import androidx.slice.widget.SliceLiveData; + +import com.android.settingslib.wifi.AccessPoint; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class WifiScanWorkerTest { + + private static final String AP_NAME = "ap"; + + private Context mContext; + private ContentResolver mResolver; + private WifiManager mWifiManager; + private WifiScanWorker mWifiScanWorker; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mResolver = mock(ContentResolver.class); + doReturn(mResolver).when(mContext).getContentResolver(); + mWifiManager = mContext.getSystemService(WifiManager.class); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + mWifiManager.setWifiEnabled(true); + + mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI); + } + + @Test + public void onWifiStateChanged_shouldNotifyChange() { + mWifiScanWorker.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLED); + + verify(mResolver).notifyChange(WIFI_SLICE_URI, null); + } + + private AccessPoint createAccessPoint(String name, State state) { + final NetworkInfo info = mock(NetworkInfo.class); + doReturn(state).when(info).getState(); + + final Bundle savedState = new Bundle(); + savedState.putString("key_ssid", name); + savedState.putParcelable("key_networkinfo", info); + return new AccessPoint(mContext, savedState); + } + + @Test + public void SliceAccessPoint_sameState_shouldBeTheSame() { + final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTED); + final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); + + assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2))) + .isTrue(); + } + + @Test + public void SliceAccessPoint_differentState_shouldBeDifferent() { + final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTING); + final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); + + assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2))) + .isFalse(); + } + + @Test + public void SliceAccessPoint_differentLength_shouldBeDifferent() { + final AccessPoint ap1 = createAccessPoint(AP_NAME, State.CONNECTED); + final AccessPoint ap2 = createAccessPoint(AP_NAME, State.CONNECTED); + final List list = new ArrayList<>(); + list.add(ap1); + list.add(ap2); + + assertThat(mWifiScanWorker.areListsTheSame(list, Arrays.asList(ap1))).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java index e9f35d8a31a..b2718fc0631 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -19,25 +19,20 @@ package com.android.settings.wifi.slice; import static android.app.slice.Slice.HINT_LIST_ITEM; import static android.app.slice.SliceItem.FORMAT_SLICE; -import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI; import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT; -import static com.android.settings.wifi.slice.WifiSlice.WifiScanWorker; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.NetworkInfo; -import android.net.NetworkInfo.State; import android.net.Uri; import android.net.wifi.WifiManager; -import android.os.Bundle; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -53,6 +48,9 @@ import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.testutils.SliceTester; import com.android.settingslib.wifi.AccessPoint; +import java.util.ArrayList; +import java.util.List; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -62,11 +60,8 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - @RunWith(RobolectricTestRunner.class) +@Config(shadows = WifiSliceTest.ShadowSliceBackgroundWorker.class) public class WifiSliceTest { private static final String AP1_NAME = "ap1"; @@ -76,7 +71,6 @@ public class WifiSliceTest { private ContentResolver mResolver; private WifiManager mWifiManager; private WifiSlice mWifiSlice; - private WifiScanWorker mWifiScanWorker; @Before public void setUp() { @@ -90,7 +84,6 @@ public class WifiSliceTest { mWifiManager.setWifiEnabled(true); mWifiSlice = new WifiSlice(mContext); - mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI); } @Test @@ -160,13 +153,12 @@ public class WifiSliceTest { } @Test - @Config(shadows = ShadowSliceBackgroundWorker.class) public void getWifiSlice_noReachableAp_shouldReturnLoadingRow() { setWorkerResults( createAccessPoint(AP1_NAME, false, false), createAccessPoint(AP2_NAME, false, false)); - final Slice wifiSlice = mWifiSlice.getSlice(); + final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); @@ -177,11 +169,10 @@ public class WifiSliceTest { } @Test - @Config(shadows = ShadowSliceBackgroundWorker.class) public void getWifiSlice_oneActiveAp_shouldReturnLoadingRow() { setWorkerResults(createAccessPoint(AP1_NAME, true, true)); - final Slice wifiSlice = mWifiSlice.getSlice(); + final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); @@ -191,13 +182,12 @@ public class WifiSliceTest { } @Test - @Config(shadows = ShadowSliceBackgroundWorker.class) public void getWifiSlice_oneActiveApAndOneUnreachableAp_shouldReturnLoadingRow() { setWorkerResults( createAccessPoint(AP1_NAME, true, true), createAccessPoint(AP2_NAME, false, false)); - final Slice wifiSlice = mWifiSlice.getSlice(); + final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); @@ -208,11 +198,10 @@ public class WifiSliceTest { } @Test - @Config(shadows = ShadowSliceBackgroundWorker.class) public void getWifiSlice_oneReachableAp_shouldNotReturnLoadingRow() { setWorkerResults(createAccessPoint(AP1_NAME, false, true)); - final Slice wifiSlice = mWifiSlice.getSlice(); + final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); @@ -222,13 +211,12 @@ public class WifiSliceTest { } @Test - @Config(shadows = ShadowSliceBackgroundWorker.class) public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() { setWorkerResults( createAccessPoint(AP1_NAME, false, true), createAccessPoint(AP2_NAME, false, true)); - final Slice wifiSlice = mWifiSlice.getSlice(); + final Slice wifiSlice = mWifiSlice.getSlice(); final List sliceItems = wifiSlice.getItems(); SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME); @@ -249,51 +237,6 @@ public class WifiSliceTest { assertThat(wifiManager.getWifiState()).isEqualTo(WifiManager.WIFI_STATE_ENABLED); } - @Test - public void onWifiStateChanged_shouldNotifyChange() { - mWifiScanWorker.onWifiStateChanged(WifiManager.WIFI_STATE_DISABLED); - - verify(mResolver).notifyChange(WIFI_SLICE_URI, null); - } - - private AccessPoint createAccessPoint(String name, State state) { - final NetworkInfo info = mock(NetworkInfo.class); - doReturn(state).when(info).getState(); - - final Bundle savedState = new Bundle(); - savedState.putString("key_ssid", name); - savedState.putParcelable("key_networkinfo", info); - return new AccessPoint(mContext, savedState); - } - - @Test - public void SliceAccessPoint_sameState_shouldBeTheSame() { - final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTED); - final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED); - - assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2))) - .isTrue(); - } - - @Test - public void SliceAccessPoint_differentState_shouldBeDifferent() { - final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTING); - final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED); - - assertThat(mWifiScanWorker.areListsTheSame(Arrays.asList(ap1), Arrays.asList(ap2))) - .isFalse(); - } - @Test - public void SliceAccessPoint_differentLength_shouldBeDifferent() { - final AccessPoint ap1 = createAccessPoint(AP1_NAME, State.CONNECTED); - final AccessPoint ap2 = createAccessPoint(AP1_NAME, State.CONNECTED); - final List list = new ArrayList<>(); - list.add(ap1); - list.add(ap2); - - assertThat(mWifiScanWorker.areListsTheSame(list, Arrays.asList(ap1))).isFalse(); - } - @Implements(SliceBackgroundWorker.class) public static class ShadowSliceBackgroundWorker { private static WifiScanWorker mWifiScanWorker = mock(WifiScanWorker.class); From a0982b39bc7a674c5f8a378b4d599d31ee51d70f Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Mon, 1 Apr 2019 18:13:17 +0800 Subject: [PATCH 08/11] Fix talkback issue which focus on entire grid When there is only one condition card, sometimes talkback will focus on entire grid. In fact, the entire grid is a recycler view which is the container to put all contextual cards. Mark the recycler view with no important for accessibility to fix it. Fixes: 128896302 Test: visual Change-Id: I177fa014ec3208001b3a490dbdf9b6b6195985ea --- res/layout/settings_homepage.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/layout/settings_homepage.xml b/res/layout/settings_homepage.xml index 6e2b302541f..5710bace5eb 100644 --- a/res/layout/settings_homepage.xml +++ b/res/layout/settings_homepage.xml @@ -25,6 +25,7 @@ android:id="@+id/card_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:layoutAnimation="@anim/layout_animation_fade_in"/> + android:layoutAnimation="@anim/layout_animation_fade_in" + android:importantForAccessibility="no"/> From 526fb9f8fbe1e94ac5d4048e9c907c0c13728354 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Fri, 29 Mar 2019 23:44:57 -0700 Subject: [PATCH 09/11] Send a broadcast when turning grayscale off When users turn grayscale off in Settings, Settings will send an intent to receivers who have requested the CONTROL_DISPLAY_COLOR_TRANSFORMS permission. We also specify FLAG_RECIEVER_INCLUDE_BACKGROUND to make sure that the intent could be sent to manifest receivers. Bug: 118387886 Test: robotests Change-Id: Ib0c959e72dd4068014951347df35409790c77ab5 --- .../conditional/GrayscaleConditionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java index 341e0612568..c6466e8d697 100644 --- a/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java +++ b/src/com/android/settings/homepage/contextualcards/conditional/GrayscaleConditionController.java @@ -113,8 +113,8 @@ public class GrayscaleConditionController implements ConditionalCardController { } private void sendBroadcast() { - final Intent intent = new Intent(); - intent.setAction(ACTION_GRAYSCALE_CHANGED); + final Intent intent = new Intent(ACTION_GRAYSCALE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mAppContext.sendBroadcast(intent, Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS); } From 3debd0691d2b51939700e5091fdc970dc7a358d4 Mon Sep 17 00:00:00 2001 From: lindatseng Date: Thu, 28 Mar 2019 14:46:36 -0700 Subject: [PATCH 10/11] Tint icon colors of storage screen same as other screen We were using hard-coded colors to tint the icons on storage screen for some reasons. Change it to the same way we tint other icons, as there's no reason to keep it special, and also per partner's request to keep it consistent with other screens. Also update the ic_sim_sd icon from png to vectors. Test: Manual/Visual verification BUG: 113977374 Change-Id: Ia0210635e936b6798aaf9e48dd8815a197396065 --- res/drawable-hdpi/ic_sim_sd.png | Bin 836 -> 0 bytes res/drawable-mdpi/ic_sim_sd.png | Bin 700 -> 0 bytes res/drawable-xhdpi/ic_sim_sd.png | Bin 912 -> 0 bytes res/drawable-xxhdpi/ic_sim_sd.png | Bin 1144 -> 0 bytes res/drawable-xxxhdpi/ic_sim_sd.png | Bin 1541 -> 0 bytes res/drawable/ic_sim_sd.xml | 27 ++++++++++++++++++ .../settings/deviceinfo/StorageSettings.java | 23 ++------------- .../deviceinfo/StorageVolumePreference.java | 14 ++++----- 8 files changed, 37 insertions(+), 27 deletions(-) delete mode 100644 res/drawable-hdpi/ic_sim_sd.png delete mode 100644 res/drawable-mdpi/ic_sim_sd.png delete mode 100644 res/drawable-xhdpi/ic_sim_sd.png delete mode 100644 res/drawable-xxhdpi/ic_sim_sd.png delete mode 100644 res/drawable-xxxhdpi/ic_sim_sd.png create mode 100644 res/drawable/ic_sim_sd.xml diff --git a/res/drawable-hdpi/ic_sim_sd.png b/res/drawable-hdpi/ic_sim_sd.png deleted file mode 100644 index 50a16dbbbb8771aa75a97c9d471e7194e7e0de2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 836 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^xl_H+M9WCijWi-X*q z7}lMWc?skwBzpw;GB8xBF)%c=FfjZA3N^f7U???UV0e|lz+eS5K)hhiu0R{01Y44~ zy9>jA5L~c#`DCC7XMsm#F_88EW4Dvpb_@(mBAzaeArXh)&N!bZY$(ufZ|PYA>C^n{e;h4#PZ)qQ})z>B-5T8)q4a-O&4x z*bx07s>&p&@|;{kPeR%OI|HV5SDx40Jh1(%&0U83jT=i27d){)&#>z7mxh%Id?t}6 zx1E3TSo%ZYuQUA@jxia`XEkP3J1{eAb?E<~oq_jVa?_bVh*hW^xYcy4aaQ`VgEfKo zRX?y*aK1V5(eQ)Q(}-H@1KJO^9#fd1zA^K$!`BmEwoclZux#GbmRAert*(7-Tl{Nh zy70X9JPUr9)iUw1KI=DJ$r+Vn)cEMO3ERFZgYKBNn?dYV7H{8O_ubYnrEXP<9+ z%N^c5(>rts6XMW$3FWx=Ti>AE#eA{!!9F0H0k<4owuQ_BZ ztgpZ6UA)xc!}R^tVvN7^Kdor!s$0sv^y>G;jK6qeJjy#8L#OPliDM|YbeQ&P{dv)X zS6^p~imERU@obNiSo>^sZP61W;n^YbKi@A|@3cXB{l@(fRsl zlJCkRA+vSnZiuqWy{cptHiD0w}V+aP=h4MhT#0PlJdl&R0hYC h{G?O`&)mfH)S%SFl*+=Bsi4%%;OXk;vd$@?2>>qoUL61c diff --git a/res/drawable-mdpi/ic_sim_sd.png b/res/drawable-mdpi/ic_sim_sd.png deleted file mode 100644 index 19266089bccd058e00733bd6d8090b8a7c629819..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 700 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPV z+ueoXKL{?^yL>WGgtNdSvKUBvfU(=jY&)RwJ)SO(As)xyPTTKy*g)V|y%}p~*EBsH z8wQRFsd>$7PIm^|^#p!`6jg6&$P?#U$QC9FA2 z*$1RIFol$!5na4c>W@$AEyia{pUGCvXIOt=`9=BAMLD5AIR1F2E}ie1{ABkz1)ZbL zjPJe84sKMGthm^G-sr;SMdvv-eT`->n_B!LkatIT@7~~FYZ<~Dr(1M;xx6^`g4eYF zt3ajDPs!g4cAWEMNqNa?tKt5@>xHuIhcgv_gnfJ;{F`+}X+|d7$@Kxp5+bZC6C|fG z@a{`4FPi?Pei7$s4~g7^5+@Y|H_2^ak74o-b78$$eTg|e-k@zdOYEY6S=kTRBIYde z;j2)${?cyF{W|`Mr~is85%Ga*Cfa9xzApWM{daEqMFU~qW55VfEpd$~Nl7e8wMs5Z z1yT$~28JfO2IjhkW+8^=Rz^lvK(>Lom4QK*Y~O7Z4Y~O#nQ4`{HT-rkO9yI@1lbUr rpH@NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-G$*l2rk&Wd@@jkv%n*=7)X17vD?XPI|c@(1Wy;okc@k8XT|1Sb`WW`U)~Vq!Q#J$ zG2~i9OM-c=i_;R3BmUlD7oIA#B(OI;XcYax(zImDAv6Bs_MXdyHOC*9Tsr06WwdYO z`#HwN&(-%aKbMJAkK)Nx0+%h)2b)zPIVG5zv~-Z zHuL!1_3VkU;(X@w4Y3bSWF8HWVSL~C{6PB0;47UO>I>%m;m+JRLvF+QdiHnB^BWkg zA0H|&uF(E@(q8maCGSF`1AGNMcUZpJd<`pDsr*Ub@U9I*o%Io;Gcy=|h$ie;y8gW( z`+!Kf%@&~t>igB76el>JxWiU=V(<0F{GffTe{we5XV}M}yz`#O{;d}{KTNJ?+B0cW z@6R9?pQpFZ`DnbAzS17*&G_}n_oaI*`Zo2Q($bMXQuk>$_Y-k#U283Ot`C#tnc2Nh z%(wi**$6iwnk8nyp%ag;k}(ekJH#+QcJL-MQ>&Km?Th`r!;?yvH* z*~Y!LP7Q@Y31So5`_^jaw9Tlz6Fb9f>7UR{d7%xmnX}wtFG;m;W3S=ZxY}7WM|1=1;h&)K=ZSTcmEXH*L$WSvpXIB*=!~{Irtt#G+IN$CUh}R0Yr6#Prml)Wnp^!jq|>oWbDf>gTe~ HDWM4faT|BB diff --git a/res/drawable-xxhdpi/ic_sim_sd.png b/res/drawable-xxhdpi/ic_sim_sd.png deleted file mode 100644 index e05e7adc25467e94031df5a97318dff7637cbf06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1144 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Ea{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPV z+ueoXKL{?^yL>WGgtNdSvKUB%?PPE}nQh0w!0h4a;uunK>+PJ=dBTPw4(FGcWF6_# zz0Y9JP{SB~giBzmRi=*utx6G_(}Kko)Umx{|8S#}+na@3!$jrqA+y36oaHtj zE&A^%htBPpbF28-uc|pWSX~39RJ@~Zck90RcIN$*9ag*dKG?oY*ksZq#>!1q3sYYk zm$G?zDX)m}TmCSr_yL2&t?O&|A7`92OWZ_HeRJ;~{yWQVdi-MD!@jNY`T_Gl(jkGD zCQ6>z$!?ReCC-`MBDr3VaeAZeACFZI+7o4R7QbWSe_MFDQe*qqFO9(mydTW4jsLl8 zwqQul92sHD{x=(~@2kw*yD!v7&x&`B@`qzb#6#|_eP8o26{y4$BpL zqQ5ryR=;sS=igOs%zSOn^^K=?I9PCQd0Q>SJxj)Nw#e?Qb_IKuEX+Qj?$gv8dTcpw zisIoK8ONtZe@-+emH+EI$!pi#cIrx6mE+OO!vddsKF>(n{wZ>0fnL~5NlvN$IbzSw zeQi(=kzD(a_vL?Q2`k^KMNX3&i z2}-HV6>0zDx>Y-5+#~q3-iN(D5Ukc8#_d}QRQ!rf*-~>E|4B~Sqkk^GQu}ac-umei zR`-}~Jf$M=goUT!^^7^jzT2fjEW}s)2stJwJ)Jex`0biM8b{f8ZEZWHA~+@anUVB1 zN4@t~7Bbg7`#f!i~AO2oW3o__%;cb{sp@fHeb1x zyEV?_j?^)E2f0l-7A8%64dp#%XY_CK2R!PDxUtVv-Sb=vgo~x``S)5Uhrpizj^jIUoK!)OztvEOmwat!;9*j(+GeI{t^TZ`a-G<|F&=12dRviEBhjN@7W> zRdP`(kYX@0Ff`FMFxNFS3o$geGBUCPvJK3w3=Fzt`);FX$jwj5OsmAL;kScXI#7co u$cEtjw370~qErUQl>DSr1<%~X^wgl##FWaylc}H*fWgz%&t;ucLK6VBY21SV diff --git a/res/drawable-xxxhdpi/ic_sim_sd.png b/res/drawable-xxxhdpi/ic_sim_sd.png deleted file mode 100644 index b12e34efdfb997fdba375785c9f2f9625e00bc39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1541 zcmbVLeNYr-9Dea6$<(YlC4y{QDV1SwcW;lo+mnOSJ5J6xB*46w99j0>gGKK4+FiT@ z^&7!Pu=Jn_5qP1(VL@+&Pc{Y3_z15FjU&q> zj*WyUE6w<*D3>!2vx*YL(lluVPibk)z-s6;9ZS=xn3kd$lBP&4OVG4QXE4!vZ0x~N zHp%TV<=gCIxzNmvmnn+hM3R9(KoejzqEt%KEX$IVmegtq6hX*UK7|VsK6!SO!3Jes z68wrF`mhKiS0Yv_W*jLUJ%ZQobdD4IG%P!=mC9^Hp$b`*@fdvKyiAW~ymA_?eHxC)!dS9+mOaoEf_+R?ZLw~1mn4_%_8 z2*${J2pyv{5S-h?5pKxqS*YU;B~TmXxgXw6X^pJio<->l6isL8j9P;(%V54fc`eI9jv^@9s3&wDK8-LMX&u2a6k~vV3FHiVd<^gY zzvhuh6d9?M|5Q$N36*1HINk?nGTuqhhuTL%eX@Stq+$Rj%yrln6b66!?ZP*U4;QPC z4DBkP5l=&KLE+E0q#a)!>tx#KY#wz>nAtnEblGq zZMyT_C#`q)Zt6?QV82Y7yMjq&p4p!&lk&67J79tS6hn&NF3? zP_|76Vq+}#AmRY1>r}1rw6+`K!H3M0-v=gG%!fgX2J~MGeLiJt_|451k}N;JxCc&e zoO!!w+39O3+3}v%j=bUOJ+MJzt^DCs!nV(He!A5C{t!LTq<{I=tj77fUhOK13*DW6 z)6v_p{&X15>gH3vKYBL1{8DOvMG}%(Gjmi1eXIqjz`$EoimQBJGui--tX$g{3yatN E1EjedqyPW_ diff --git a/res/drawable/ic_sim_sd.xml b/res/drawable/ic_sim_sd.xml new file mode 100644 index 00000000000..d7e31a24809 --- /dev/null +++ b/res/drawable/ic_sim_sd.xml @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java index 2e52e6b4642..a24f82b3535 100644 --- a/src/com/android/settings/deviceinfo/StorageSettings.java +++ b/src/com/android/settings/deviceinfo/StorageSettings.java @@ -23,8 +23,6 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; @@ -76,16 +74,6 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index private static final String TAG_DISK_INIT = "disk_init"; private static final int METRICS_CATEGORY = SettingsEnums.DEVICEINFO_STORAGE; - static final int COLOR_PUBLIC = Color.parseColor("#ff9e9e9e"); - - static final int[] COLOR_PRIVATE = new int[]{ - Color.parseColor("#ff26a69a"), - Color.parseColor("#ffab47bc"), - Color.parseColor("#fff2a600"), - Color.parseColor("#ffec407a"), - Color.parseColor("#ffc0ca33"), - }; - private StorageManager mStorageManager; private PreferenceCategory mInternalCategory; @@ -176,13 +164,12 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index if (vol.getType() == VolumeInfo.TYPE_PRIVATE) { final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol, sTotalInternalStorage); - final int color = COLOR_PRIVATE[privateCount++ % COLOR_PRIVATE.length]; mInternalCategory.addPreference( - new StorageVolumePreference(context, vol, color, volumeTotalBytes)); + new StorageVolumePreference(context, vol, volumeTotalBytes)); } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC || vol.getType() == VolumeInfo.TYPE_STUB) { mExternalCategory.addPreference( - new StorageVolumePreference(context, vol, COLOR_PUBLIC, 0)); + new StorageVolumePreference(context, vol, 0)); } } @@ -192,15 +179,11 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index if (rec.getType() == VolumeInfo.TYPE_PRIVATE && mStorageManager.findVolumeByUuid(rec.getFsUuid()) == null) { // TODO: add actual storage type to record - final Drawable icon = context.getDrawable(R.drawable.ic_sim_sd); - icon.mutate(); - icon.setTint(COLOR_PUBLIC); - final Preference pref = new Preference(context); pref.setKey(rec.getFsUuid()); pref.setTitle(rec.getNickname()); pref.setSummary(com.android.internal.R.string.ext_media_status_missing); - pref.setIcon(icon); + pref.setIcon(R.drawable.ic_sim_sd); mInternalCategory.addPreference(pref); } } diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java index 14fd42e23d4..0734c0b66ad 100644 --- a/src/com/android/settings/deviceinfo/StorageVolumePreference.java +++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java @@ -50,16 +50,16 @@ public class StorageVolumePreference extends Preference { private final StorageManager mStorageManager; private final VolumeInfo mVolume; - private int mColor; private int mUsedPercent = -1; + private ColorStateList mColorTintList; // TODO: ideally, VolumeInfo should have a total physical size. - public StorageVolumePreference(Context context, VolumeInfo volume, int color, long totalBytes) { + public StorageVolumePreference(Context context, VolumeInfo volume, long totalBytes) { super(context); mStorageManager = context.getSystemService(StorageManager.class); mVolume = volume; - mColor = color; + mColorTintList = Utils.getColorAttr(context, android.R.attr.colorControlNormal); setLayoutResource(R.layout.storage_volume); @@ -107,8 +107,10 @@ public class StorageVolumePreference extends Preference { } if (freeBytes < mStorageManager.getStorageLowBytes(path)) { - mColor = Utils.getColorAttrDefaultColor(context, android.R.attr.colorError); + mColorTintList = Utils.getColorAttr(context, android.R.attr.colorError); icon = context.getDrawable(R.drawable.ic_warning_24dp); + icon.mutate(); + icon.setTintList(mColorTintList); } } else { @@ -116,8 +118,6 @@ public class StorageVolumePreference extends Preference { mUsedPercent = -1; } - icon.mutate(); - icon.setTint(mColor); setIcon(icon); if (volume.getType() == VolumeInfo.TYPE_PUBLIC @@ -138,7 +138,7 @@ public class StorageVolumePreference extends Preference { if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE && mUsedPercent != -1) { progress.setVisibility(View.VISIBLE); progress.setProgress(mUsedPercent); - progress.setProgressTintList(ColorStateList.valueOf(mColor)); + progress.setProgressTintList(mColorTintList); } else { progress.setVisibility(View.GONE); } From c6d79cd0da5d06f8e1ed685f135837c73bef2011 Mon Sep 17 00:00:00 2001 From: Zoran Jovanovic Date: Sun, 17 Feb 2019 22:54:47 +0100 Subject: [PATCH 11/11] Don't allow RRO uninstall if overlay is enabled System RROs can never be uninstalled. Also, enabled RRO, i.e. RRO applied to their target packages, must not be uninstalled by end-user because that may be dangerous to the configuration of its target package. Disabled RROs, i.e. RRO not applied to their target packages, are free to be uninstalled to reclaim space. Bug: 124556507 Test: manual + `make RunSettingsRoboTests ROBOTEST_FILTER=AppButtonsPreferenceControllerTest` Change-Id: Ib6bd2765c8cb88a5887de817a08a1541eaee0cab --- .../AppButtonsPreferenceController.java | 26 ++++++++ .../appinfo/AppInfoDashboardFragment.java | 12 +++- .../AppButtonsPreferenceControllerTest.java | 59 +++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java index 56d53358aa5..7339f2132b2 100644 --- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java @@ -25,6 +25,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.om.OverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -103,6 +105,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp private final int mRequestRemoveDeviceAdmin; private final DevicePolicyManager mDpm; private final UserManager mUserManager; + private final OverlayManager mOverlayManager; private final PackageManager mPm; private final SettingsActivity mActivity; private final InstrumentedPreferenceFragment mFragment; @@ -136,6 +139,7 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); mPm = activity.getPackageManager(); + mOverlayManager = activity.getSystemService(OverlayManager.class); mPackageName = packageName; mActivity = activity; mFragment = fragment; @@ -435,6 +439,28 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp enabled = false; } + // Resource overlays can be uninstalled iff they are public + // (installed on /data) and disabled. ("Enabled" means they + // are in use by resource management.) If they are + // system/vendor, they can never be uninstalled. :-( + if (mAppEntry.info.isResourceOverlay()) { + if (isBundled) { + enabled = false; + } else { + String pkgName = mAppEntry.info.packageName; + UserHandle user = UserHandle.getUserHandleForUid(mAppEntry.info.uid); + OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(pkgName, user); + if (overlayInfo != null && overlayInfo.isEnabled()) { + ApplicationsState.AppEntry targetEntry = + mState.getEntry(overlayInfo.targetPackageName, + UserHandle.getUserId(mAppEntry.info.uid)); + if (targetEntry != null) { + enabled = false; + } + } + } + } + mButtonsPref.setButton2Enabled(enabled); } diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 183d3843447..766fa50acc5 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -640,10 +640,18 @@ public class AppInfoDashboardFragment extends DashboardFragment final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + if (mFinishing) { + return; + } + final String packageName = intent.getData().getSchemeSpecificPart(); - if (!mFinishing && (mAppEntry == null || mAppEntry.info == null - || TextUtils.equals(mAppEntry.info.packageName, packageName))) { + if (mAppEntry == null + || mAppEntry.info == null + || TextUtils.equals(mAppEntry.info.packageName, packageName)) { onPackageRemoved(); + } else if (mAppEntry.info.isResourceOverlay() + && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) { + refreshUi(); } } }; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java index 6d9430ccd26..ff33d26ea21 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java @@ -39,9 +39,12 @@ import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; +import android.content.om.OverlayManager; +import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.os.RemoteException; import android.os.UserManager; import android.view.View; @@ -70,11 +73,15 @@ import org.robolectric.util.ReflectionHelpers; public class AppButtonsPreferenceControllerTest { private static final String PACKAGE_NAME = "com.android.settings"; + private static final String RRO_PACKAGE_NAME = "com.android.settings.overlay"; private static final String RESOURCE_STRING = "string"; private static final boolean ALL_USERS = false; private static final boolean DISABLE_AFTER_INSTALL = true; private static final int REQUEST_UNINSTALL = 0; private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; + private static final OverlayInfo OVERLAY_DISABLED = createFakeOverlay("overlay", false, 1); + private static final OverlayInfo OVERLAY_ENABLED = createFakeOverlay("overlay", true, 1); + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private SettingsActivity mSettingsActivity; @Mock @@ -88,6 +95,8 @@ public class AppButtonsPreferenceControllerTest { @Mock private ApplicationInfo mAppInfo; @Mock + private OverlayManager mOverlayManager; + @Mock private PackageManager mPackageManger; @Mock private DevicePolicyManager mDpm; @@ -113,6 +122,8 @@ public class AppButtonsPreferenceControllerTest { doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE); doReturn(mPackageManger).when(mSettingsActivity).getPackageManager(); doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE); + doReturn(mOverlayManager).when(mSettingsActivity). + getSystemService(OverlayManager.class); doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt()); when(mSettingsActivity.getApplication()).thenReturn(mApplication); when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(RESOURCE_STRING); @@ -276,6 +287,41 @@ public class AppButtonsPreferenceControllerTest { verify(mButtonPrefs).setButton2Enabled(false); } + @Test + public void updateUninstallButton_isSystemRro_setButtonDisable() { + mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + + when(mAppInfo.isResourceOverlay()).thenReturn(true); + + mController.updateUninstallButton(); + + verify(mButtonPrefs).setButton2Enabled(false); + } + + @Test + public void updateUninstallButton_isNonSystemRro_setButtonDisable() + throws RemoteException { + when(mAppInfo.isResourceOverlay()).thenReturn(true); + when(mOverlayManager.getOverlayInfo(anyString(), any())) + .thenReturn(OVERLAY_ENABLED); + + mController.updateUninstallButton(); + + verify(mButtonPrefs).setButton2Enabled(false); + } + + @Test + public void updateUninstallButton_isNonSystemRro_setButtonEnable() + throws RemoteException { + when(mAppInfo.isResourceOverlay()).thenReturn(true); + when(mOverlayManager.getOverlayInfo(anyString(), any())) + .thenReturn(OVERLAY_DISABLED); + + mController.updateUninstallButton(); + + verify(mButtonPrefs).setButton2Enabled(true); + } + @Test public void updateForceStopButton_HasActiveAdmins_setButtonDisable() { doReturn(true).when(mDpm).packageHasActiveAdmins(anyString()); @@ -418,4 +464,17 @@ public class AppButtonsPreferenceControllerTest { return pref; } + + private static OverlayInfo createFakeOverlay(String pkg, boolean enabled, int priority) { + final int state = (enabled) ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED; + return new OverlayInfo(pkg /* packageName */, + "target.package" /* targetPackageName */, + "theme" /* targetOverlayableName */, + "category", /* category */ + "package", /* baseCodePath */ + state, + 0 /* userId */, + priority, + false /* isStatic */); + } }