From 9fdbe80f8b5ea80d8f457123273d031929374146 Mon Sep 17 00:00:00 2001 From: pastychang Date: Fri, 15 Mar 2019 19:39:32 +0800 Subject: [PATCH 01/27] Apply a11y screen to glif layout Screenshot: https://hsv.googleplex.com/5745610682531840 Test: atest Bug: 126065441 Change-Id: I31f39d5d78a80e6badb92a4e79df261eeeeaa4d4 --- AndroidManifest.xml | 2 +- .../AccessibilitySettingsForSetupWizard.java | 23 ++++++++++ ...ibilitySettingsForSetupWizardActivity.java | 8 ---- ...itySettingsForSetupWizardActivityTest.java | 44 +++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7625e78530b..4b23e2bb2ac 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1382,7 +1382,7 @@ + android:theme="@style/GlifTheme.Light"> diff --git a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java index 46a6371f545..16f5fcdd245 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java +++ b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java @@ -23,13 +23,19 @@ import android.content.Context; import android.content.pm.ServiceInfo; import android.os.Bundle; import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import androidx.preference.Preference; +import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; +import com.google.android.setupdesign.GlifPreferenceLayout; + import java.util.List; /** @@ -63,6 +69,23 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm return SettingsEnums.SUW_ACCESSIBILITY; } + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + GlifPreferenceLayout layout = (GlifPreferenceLayout) view; + layout.setDividerInsets(Integer.MAX_VALUE, 0); + + layout.setHeaderText(R.string.vision_settings_title); + } + + @Override + public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, + Bundle savedInstanceState) { + GlifPreferenceLayout layout = (GlifPreferenceLayout) parent; + return layout.onCreateRecyclerView(inflater, parent, savedInstanceState); + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); diff --git a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivity.java b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivity.java index 9512f011d97..fc68d2caf04 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivity.java +++ b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivity.java @@ -33,14 +33,6 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit private static final String SAVE_KEY_TITLE = "activity_title"; - @Override - protected void onCreate(Bundle savedState) { - super.onCreate(savedState); - - // Finish configuring the content view. - getActionBar().setDisplayHomeAsUpEnabled(true); - } - @Override protected void onSaveInstanceState(Bundle savedState) { savedState.putCharSequence(SAVE_KEY_TITLE, getTitle()); diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java new file mode 100644 index 00000000000..d6b12c35a32 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardActivityTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Intent; + +import androidx.test.filters.SmallTest; + +import com.android.settings.R; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +@SmallTest +public class AccessibilitySettingsForSetupWizardActivityTest { + + @Test + public void createSetupAccessibilityActivity_shouldBeSUWTheme() { + final Intent intent = new Intent(); + AccessibilitySettingsForSetupWizardActivity activity = + Robolectric.buildActivity(AccessibilitySettingsForSetupWizardActivity.class, intent).get(); + + assertThat(activity.getThemeResId()).isEqualTo(R.style.GlifTheme_Light); + } +} From 1c60b800840032773acb00d3baf809c41dff3e89 Mon Sep 17 00:00:00 2001 From: Matthew Fritze Date: Wed, 20 Mar 2019 12:28:25 -0700 Subject: [PATCH 02/27] Fix Slices in Panel no-op bug Slices in panels stopped changing the underlying settings. It is not always reproducible, but the error happend when PendingIntents started failing to fire, and thus not connecting with the Broadcast Receiver in Settings dedicated to handling Slice changes. This fix always highlighted that we shouldn't be using TaskAffinity, because there is no guarantee 3p apps don't spoof it. In order to fix both the PendingIntent issue, and to de-dupe activities from Settings vs Settings Panels: we use 'documentLaunchMode=always' instead. Fixes: 129006165 Test: manual Test: atest SettingsPanelTest Change-Id: If94dd719a2e33c85c04a416868f565f719941234 --- AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4bb1a8c530a..e3a0dec8037 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3016,9 +3016,8 @@ From 10d648af3bcc80cb46eab30bbf3e5cbd3bb477c1 Mon Sep 17 00:00:00 2001 From: cosmohsieh Date: Mon, 25 Mar 2019 16:26:19 +0800 Subject: [PATCH 03/27] Fix inconsistent border width in QR code scanning camera 1) Fix border width become larger in the corner. 2) Correct border width Bug: 127654782 Test: manual Change-Id: I792ac571b5c59d14af82848698d76cb59a2f4249 --- .../android/settings/wifi/qrcode/QrDecorateView.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/wifi/qrcode/QrDecorateView.java b/src/com/android/settings/wifi/qrcode/QrDecorateView.java index 2be1eaba0dc..5df0cd0a7c2 100644 --- a/src/com/android/settings/wifi/qrcode/QrDecorateView.java +++ b/src/com/android/settings/wifi/qrcode/QrDecorateView.java @@ -46,6 +46,7 @@ public class QrDecorateView extends View { final private Paint mBackgroundPaint; final private float mRadius; + final private float mInnerRidus; private Bitmap mMaskBitmap; private Canvas mMaskCanvas; @@ -73,6 +74,9 @@ public class QrDecorateView extends View { mFocused = false; mRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, getResources().getDisplayMetrics()); + // Inner radius needs to minus stroke width for keeping the width of border consistent. + mInnerRidus = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + CORNER_RADIUS - CORNER_STROKE_WIDTH, getResources().getDisplayMetrics()); mCornerColor = context.getResources().getColor(R.color.qr_corner_line_color); mFocusedCornerColor = context.getResources().getColor(R.color.qr_focused_corner_line_color); @@ -111,7 +115,7 @@ public class QrDecorateView extends View { // Draw outer corner. mMaskCanvas.drawRoundRect(mOuterFrame, mRadius, mRadius, mStrokePaint); // Draw inner transparent corner. - mMaskCanvas.drawRoundRect(mInnerFrame, mRadius, mRadius, mTransparentPaint); + mMaskCanvas.drawRoundRect(mInnerFrame, mInnerRidus, mInnerRidus, mTransparentPaint); canvas.drawBitmap(mMaskBitmap, 0, 0, mBackgroundPaint); super.onDraw(canvas); @@ -123,7 +127,7 @@ public class QrDecorateView extends View { final float cornerLineLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_LINE_LENGTH, getResources().getDisplayMetrics()) / 2; final float strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - CORNER_STROKE_WIDTH, getResources().getDisplayMetrics()) / 2; + CORNER_STROKE_WIDTH, getResources().getDisplayMetrics()); mOuterFrame = new RectF(centralX - cornerLineLength, centralY - cornerLineLength, centralX + cornerLineLength, centralY + cornerLineLength); @@ -131,7 +135,7 @@ public class QrDecorateView extends View { mOuterFrame.right - strokeWidth, mOuterFrame.bottom - strokeWidth); } - // Draws green lines if focued. Otherwise, draws white lines. + // Draws green lines if focused. Otherwise, draws white lines. public void setFocused(boolean focused) { mFocused = focused; invalidate(); From fefc66853ba35357eb6231fdcf66563a69d2787f Mon Sep 17 00:00:00 2001 From: pastychang Date: Thu, 21 Mar 2019 19:42:39 +0800 Subject: [PATCH 04/27] Set attribute sucUsePartnerResource Add the attribute for all pages apply gliflayout of setupdesign library. In addition, set the value false to remove stencil theme customization for outside setupwizard flow. Test: atest Bug: 128961334 Bug: 128364683 Change-Id: If55e9bf97970a5cd08d8d426747c483d80565559 --- res/layout/encryption_interstitial.xml | 1 - res/layout/face_enroll_enrolling.xml | 1 - res/layout/face_enroll_finish.xml | 1 - res/layout/fingerprint_enroll_enrolling_base.xml | 1 - res/layout/fingerprint_enroll_find_sensor_base.xml | 1 - res/layout/fingerprint_enroll_finish_base.xml | 1 - res/layout/fingerprint_enroll_introduction.xml | 1 - res/layout/storage_wizard_generic.xml | 1 - res/layout/storage_wizard_init.xml | 1 - res/layout/storage_wizard_progress.xml | 1 - res/values/styles.xml | 5 +++++ src/com/android/settings/SettingsActivity.java | 7 +++++++ .../android/settings/biometrics/BiometricEnrollBase.java | 2 ++ src/com/android/settings/deviceinfo/StorageWizardBase.java | 7 +++++++ 14 files changed, 21 insertions(+), 10 deletions(-) diff --git a/res/layout/encryption_interstitial.xml b/res/layout/encryption_interstitial.xml index 3e227cb1d6f..a2305f875f0 100644 --- a/res/layout/encryption_interstitial.xml +++ b/res/layout/encryption_interstitial.xml @@ -17,7 +17,6 @@ diff --git a/res/layout/face_enroll_enrolling.xml b/res/layout/face_enroll_enrolling.xml index e7af493a42e..7e1863b8b6a 100644 --- a/res/layout/face_enroll_enrolling.xml +++ b/res/layout/face_enroll_enrolling.xml @@ -17,7 +17,6 @@ diff --git a/res/layout/storage_wizard_init.xml b/res/layout/storage_wizard_init.xml index d02e871d09f..8d9870f1cec 100644 --- a/res/layout/storage_wizard_init.xml +++ b/res/layout/storage_wizard_init.xml @@ -16,7 +16,6 @@ diff --git a/res/layout/storage_wizard_progress.xml b/res/layout/storage_wizard_progress.xml index cf5bc437ac1..577ec3cbfc8 100644 --- a/res/layout/storage_wizard_progress.xml +++ b/res/layout/storage_wizard_progress.xml @@ -16,7 +16,6 @@ diff --git a/res/values/styles.xml b/res/values/styles.xml index 8fde9a05310..02db995d39b 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -533,4 +533,9 @@ 8dp + + diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 001e65be8be..92a0f73baf5 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -27,6 +27,7 @@ import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources.Theme; import android.os.AsyncTask; import android.os.Bundle; import android.os.UserHandle; @@ -333,6 +334,12 @@ public class SettingsActivity extends SettingsBaseActivity } } + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) { + theme.applyStyle(R.style.SetupWizardPartnerResource, true); + super.onApplyThemeResource(theme, resid, first); + } + @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java index 9c9f4fad030..a9619212988 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollBase.java +++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java @@ -21,6 +21,7 @@ import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; import android.annotation.Nullable; import android.content.Intent; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.graphics.Color; import android.os.Bundle; import android.os.UserHandle; @@ -97,6 +98,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity { @Override protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) { resid = SetupWizardUtils.getTheme(getIntent()); + theme.applyStyle(R.style.SetupWizardPartnerResource, true); super.onApplyThemeResource(theme, resid, first); } diff --git a/src/com/android/settings/deviceinfo/StorageWizardBase.java b/src/com/android/settings/deviceinfo/StorageWizardBase.java index 48cdeebcabd..38733891901 100644 --- a/src/com/android/settings/deviceinfo/StorageWizardBase.java +++ b/src/com/android/settings/deviceinfo/StorageWizardBase.java @@ -24,6 +24,7 @@ import static com.android.settings.deviceinfo.StorageSettings.TAG; import android.annotation.LayoutRes; import android.annotation.NonNull; import android.content.Intent; +import android.content.res.Resources.Theme; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.SystemClock; @@ -123,6 +124,12 @@ public abstract class StorageWizardBase extends FragmentActivity { super.onDestroy(); } + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) { + theme.applyStyle(R.style.SetupWizardPartnerResource, true); + super.onApplyThemeResource(theme, resid, first); + } + protected FooterButton getBackButton() { return mBack; } From 22ab71c7c6382e9563e9d3ba5883034d9ca1cbd2 Mon Sep 17 00:00:00 2001 From: Joel Galenson Date: Thu, 21 Mar 2019 10:00:20 -0700 Subject: [PATCH 05/27] Change the permission bar chart to use the new design - Each bar is now labeled with the permission name and the subtitle is slightly different. - Change some string for new design Test: View screen. Test: atest PermissionBarChartPreferenceControllerTest Change-Id: Ia82f9cbb6255d93c38a27b038ae5af3f066eec28 --- res/values/strings.xml | 14 ++++++-------- .../PermissionBarChartPreferenceController.java | 9 +++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 9b23ca9a1b1..2137312f283 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10916,17 +10916,15 @@ 0 apps used permissions - Most-used permissions in last 24 hours - - In last 24 hr, apps on this device accessed + Permission usage in last 24 hours - View Permissions Dashboard + See all in Dashboard - - See all permission usage in Dashboard - - + %1$d other permissions + + 1 app + %s apps + Accessibility usage diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java index 7c55b516dbf..b47ad9cc224 100644 --- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java +++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java @@ -163,12 +163,13 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro for (int index = 0; index < barViewInfos.length; index++) { final RuntimePermissionUsageInfo permissionGroupInfo = usageInfos.get(index); + final int count = permissionGroupInfo.getAppAccessCount(); + final CharSequence permLabel = getPermissionGroupLabel(permissionGroupInfo.getName()); barViewInfos[index] = new BarViewInfo( - getPermissionGroupIcon(permissionGroupInfo.getName()), - permissionGroupInfo.getAppAccessCount(), - R.string.storage_detail_apps, - getPermissionGroupLabel(permissionGroupInfo.getName())); + getPermissionGroupIcon(permissionGroupInfo.getName()), count, permLabel, + mContext.getResources().getQuantityString(R.plurals.permission_bar_chart_label, + count, count), permLabel); // Set the click listener for each bar view. // The listener will navigate user to permission usage app. From 62608f6fe7d400d11b445e774d639656ee62d0c2 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Tue, 26 Mar 2019 18:18:48 +0800 Subject: [PATCH 06/27] Optimize latency of NotificationChannelSlice - Use NotificationBackend.getChannel() in onNotifyChange(). - Record AppRow and use it in getEnabledChannels(). Bug: 123065955 Test: robotests Change-Id: I92466d31544128f09d599d86fb265997207eb55c --- .../slices/NotificationChannelSlice.java | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java index c319dca8c5a..d174156dc67 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java @@ -131,6 +131,7 @@ public class NotificationChannelSlice implements CustomSliceable { protected final Context mContext; @VisibleForTesting NotificationBackend mNotificationBackend; + private NotificationBackend.AppRow mAppRow; private String mPackageName; private int mUid; @@ -200,21 +201,12 @@ public class NotificationChannelSlice implements CustomSliceable { final String packageName = intent.getStringExtra(PACKAGE_NAME); final int uid = intent.getIntExtra(PACKAGE_UID, -1); final String channelId = intent.getStringExtra(CHANNEL_ID); - final PackageInfo packageInfo = getPackageInfo(packageName); - final NotificationBackend.AppRow appRow = mNotificationBackend.loadAppRow(mContext, - mContext.getPackageManager(), packageInfo); - - final List notificationChannels = getEnabledChannels(packageName, uid, - appRow); - for (NotificationChannel channel : notificationChannels) { - if (TextUtils.equals(channel.getId(), channelId)) { - final int importance = newState ? IMPORTANCE_LOW : IMPORTANCE_NONE; - channel.setImportance(importance); - channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); - mNotificationBackend.updateChannel(packageName, uid, channel); - return; - } - } + final NotificationChannel channel = mNotificationBackend.getChannel(packageName, uid, + channelId); + final int importance = newState ? IMPORTANCE_LOW : IMPORTANCE_NONE; + channel.setImportance(importance); + channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); + mNotificationBackend.updateChannel(packageName, uid, channel); } @Override @@ -287,16 +279,14 @@ public class NotificationChannelSlice implements CustomSliceable { private List getNotificationChannelRows(PackageInfo packageInfo, IconCompat icon) { final List notificationChannelRows = new ArrayList<>(); - final NotificationBackend.AppRow appRow = mNotificationBackend.loadAppRow(mContext, - mContext.getPackageManager(), packageInfo); final List enabledChannels = getEnabledChannels(mPackageName, mUid, - appRow); + mAppRow); for (NotificationChannel channel : enabledChannels) { notificationChannelRows.add(new ListBuilder.RowBuilder() .setTitle(channel.getName()) .setSubtitle(NotificationBackend.getSentSummary( - mContext, appRow.sentByChannel.get(channel.getId()), false)) + mContext, mAppRow.sentByChannel.get(channel.getId()), false)) .setPrimaryAction(buildRowSliceAction(channel, icon)) .addEndItem(SliceAction.createToggle(getToggleIntent(channel.getId()), null /* actionTitle */, channel.getImportance() != IMPORTANCE_NONE))); @@ -407,6 +397,7 @@ public class NotificationChannelSlice implements CustomSliceable { && sentCount > maxSentCount) { maxSentCount = sentCount; maxSentCountPackage = packageInfo; + mAppRow = appRow; } } From 69eb99109242ced6a1231b1ec9e5d6fc975f8489 Mon Sep 17 00:00:00 2001 From: Tarandeep Singh Date: Tue, 26 Mar 2019 11:26:58 -0700 Subject: [PATCH 07/27] Fix dupicate preference in personal dict Duplicate preference is added on the UserDictionaryListPreferenceController. With this change, we check before adding. Fix: 129079798 Test: Manually using steps mentioned in bug Change-Id: If73880f3be460f377b8985a44fc9f9fbe6370c06 --- .../inputmethod/UserDictionaryListPreferenceController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/inputmethod/UserDictionaryListPreferenceController.java b/src/com/android/settings/inputmethod/UserDictionaryListPreferenceController.java index 4a409e1c789..9343493de2f 100644 --- a/src/com/android/settings/inputmethod/UserDictionaryListPreferenceController.java +++ b/src/com/android/settings/inputmethod/UserDictionaryListPreferenceController.java @@ -174,7 +174,10 @@ public class UserDictionaryListPreferenceController extends BasePreferenceContro mScreen.addPreference(createUserDictionaryPreference(null)); } else { for (String locale : localeSet) { - mScreen.addPreference(createUserDictionaryPreference(locale)); + final Preference pref = createUserDictionaryPreference(locale); + if (mScreen.findPreference(pref.getKey()) == null) { + mScreen.addPreference(pref); + } } } } From 1a90162afd2a1d9f9e1a3f311b0a11627c1d29e8 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 31 Jan 2019 17:33:48 -0800 Subject: [PATCH 08/27] Address API council comments. Bug: 123587501 Test: Manual Change-Id: I72582ca6d8da8b1fe7bc4fc1b4b4db1fad6ca7a7 --- .../settings/applications/ApplicationFeatureProviderImpl.java | 2 +- .../applications/ApplicationFeatureProviderImplTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index 4bda99e98c6..55e85f93f2c 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -145,7 +145,7 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide mContext.getString(R.string.config_settingsintelligence_package_name)); final LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); - final String locationHistoryPackage = locationManager.getLocationControllerExtraPackage(); + final String locationHistoryPackage = locationManager.getExtraLocationControllerPackage(); if (locationHistoryPackage != null) { keepEnabledPackages.add(locationHistoryPackage); } diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java index ebbf5e0269f..e22b29bcd0f 100644 --- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java @@ -272,7 +272,7 @@ public final class ApplicationFeatureProviderImplTest { // Spy the real context to mock LocationManager. Context spyContext = spy(RuntimeEnvironment.application); - when(mLocationManager.getLocationControllerExtraPackage()).thenReturn(testLocationHistory); + when(mLocationManager.getExtraLocationControllerPackage()).thenReturn(testLocationHistory); when(spyContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager); ReflectionHelpers.setField(mProvider, "mContext", spyContext); From 02e45574a37ce1dae136a9e5393ab839154083e9 Mon Sep 17 00:00:00 2001 From: Raff Tsai Date: Wed, 30 Jan 2019 11:14:20 +0800 Subject: [PATCH 09/27] Add SettingsIntelligenceLogwriter Bug: 124701288 Test: robolectric, manual Change-Id: Iea446ae600d22ed62c5ee45afd1cd27a89b5de34 --- Android.mk | 1 + protos/Android.bp | 9 + protos/settings_log_bridge.proto | 37 ++++ .../SettingsIntelligenceLogWriter.java | 190 ++++++++++++++++++ .../SettingsMetricsFeatureProvider.java | 3 + tests/robotests/Android.mk | 1 + .../SettingsIntelligenceLogWriterTest.java | 78 +++++++ 7 files changed, 319 insertions(+) create mode 100644 protos/settings_log_bridge.proto create mode 100644 src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java create mode 100644 tests/robotests/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriterTest.java diff --git a/Android.mk b/Android.mk index e385b340514..906cfc77432 100644 --- a/Android.mk +++ b/Android.mk @@ -47,6 +47,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ guava \ jsr305 \ settings-contextual-card-protos-lite \ + settings-log-bridge-protos-lite \ contextualcards \ settings-logtags \ zxing-core-1.7 diff --git a/protos/Android.bp b/protos/Android.bp index 533dbca6564..5184218df63 100644 --- a/protos/Android.bp +++ b/protos/Android.bp @@ -5,4 +5,13 @@ java_library_static { type: "lite", }, srcs: ["contextual_card_list.proto"], +} + +java_library_static { + name: "settings-log-bridge-protos-lite", + host_supported: true, + proto: { + type: "lite", + }, + srcs: ["settings_log_bridge.proto"], } \ No newline at end of file diff --git a/protos/settings_log_bridge.proto b/protos/settings_log_bridge.proto new file mode 100644 index 00000000000..7b28e0d0d0a --- /dev/null +++ b/protos/settings_log_bridge.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + +package com.android.settings.intelligence; +option java_outer_classname = "LogProto"; + +message SettingsLog { + /** + * Where this SettingsUIChange event comes from. For example, if + * it's a PAGE_VISIBLE event, where the page is opened from. + */ + optional int32 attribution = 1; + + /** + * What the UI action is. + */ + optional int32 action = 2; + + /** + * Where the action is happening + */ + optional int32 page_id = 3; + + /** + * What preference changed in this event. + */ + optional string changed_preference_key = 4; + + /** + * The new value of the changed preference. + */ + optional int32 changed_preference_int_value = 5; + + /** + * The timestamp of a log event + */ + optional string timestamp = 6; +} diff --git a/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java b/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java new file mode 100644 index 00000000000..9498732e528 --- /dev/null +++ b/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriter.java @@ -0,0 +1,190 @@ +/* + * 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.core.instrumentation; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; +import android.os.UserHandle; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.Log; +import android.util.Pair; + +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.intelligence.LogProto.SettingsLog; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.LogWriter; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.LinkedList; +import java.util.List; + +public class SettingsIntelligenceLogWriter implements LogWriter { + private static final String TAG = "IntelligenceLogWriter"; + + private static final String LOG = "logs"; + private static final long MESSAGE_DELAY = DateUtils.MINUTE_IN_MILLIS; // 1 minute + + private List mSettingsLogList; + private SendLogHandler mLogHandler; + + public SettingsIntelligenceLogWriter() { + mSettingsLogList = new LinkedList<>(); + final HandlerThread workerThread = new HandlerThread("SettingsIntelligenceLogWriter", + Process.THREAD_PRIORITY_BACKGROUND); + workerThread.start(); + mLogHandler = new SendLogHandler(workerThread.getLooper()); + } + + @Override + public void visible(Context context, int attribution, int pageId) { + action(attribution /* attribution */, + SettingsEnums.PAGE_VISIBLE /* action */, + pageId /* pageId */, + "" /* changedPreferenceKey */, + 0 /* changedPreferenceIntValue */); + } + + @Override + public void hidden(Context context, int pageId) { + action(SettingsEnums.PAGE_UNKNOWN /* attribution */, + SettingsEnums.PAGE_HIDE /* action */, + pageId /* pageId */, + "" /* changedPreferenceKey */, + 0 /* changedPreferenceIntValue */); + } + + @Override + public void action(Context context, int action, Pair... taggedData) { + action(SettingsEnums.PAGE_UNKNOWN /* attribution */, + action, + SettingsEnums.PAGE_UNKNOWN /* pageId */, + "" /* changedPreferenceKey */, + 0 /* changedPreferenceIntValue */); + } + + @Override + public void action(Context context, int action, int value) { + action(SettingsEnums.PAGE_UNKNOWN /* attribution */, + action, + SettingsEnums.PAGE_UNKNOWN /* pageId */, + "" /* changedPreferenceKey */, + value /* changedPreferenceIntValue */); + } + + @Override + public void action(Context context, int action, boolean value) { + action(SettingsEnums.PAGE_UNKNOWN /* attribution */, + action, + SettingsEnums.PAGE_UNKNOWN /* pageId */, + "" /* changedPreferenceKey */, + value ? 1 : 0 /* changedPreferenceIntValue */); + } + + @Override + public void action(Context context, int action, String pkg) { + action(SettingsEnums.PAGE_UNKNOWN /* attribution */, + action, + SettingsEnums.PAGE_UNKNOWN /* pageId */, + pkg /* changedPreferenceKey */, + 1 /* changedPreferenceIntValue */); + } + + @Override + public void action(int attribution, int action, int pageId, String key, int value) { + final ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault()); + final SettingsLog settingsLog = SettingsLog.newBuilder() + .setAttribution(attribution) + .setAction(action) + .setPageId(pageId) + .setChangedPreferenceKey(key != null ? key : "") + .setChangedPreferenceIntValue(value) + .setTimestamp(now.toString()) + .build(); + mLogHandler.post(() -> { + mSettingsLogList.add(settingsLog); + }); + mLogHandler.scheduleSendLog(); + } + + @VisibleForTesting + static byte[] serialize(List settingsLogs) { + final int size = settingsLogs.size(); + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final DataOutputStream output = new DataOutputStream(bout); + // Data is "size, length, bytearray, length, bytearray ..." + try { + output.writeInt(size); + for (SettingsLog settingsLog : settingsLogs) { + final byte[] data = settingsLog.toByteArray(); + output.writeInt(data.length); + output.write(data); + } + return bout.toByteArray(); + } catch (Exception e) { + Log.e(TAG, "serialize error", e); + return null; + } finally { + try { + output.close(); + } catch (Exception e) { + Log.e(TAG, "close error", e); + } + } + } + + private class SendLogHandler extends Handler { + + public SendLogHandler(Looper looper) { + super(looper); + } + + public void scheduleSendLog() { + removeCallbacks(mSendLogsRunnable); + postDelayed(mSendLogsRunnable, MESSAGE_DELAY); + } + } + + private final Runnable mSendLogsRunnable = () -> { + final Context context = FeatureFactory.getAppContext(); + if (context == null) { + Log.e(TAG, "context is null"); + return; + } + final String action = context.getString(R.string + .config_settingsintelligence_log_action); + if (!TextUtils.isEmpty(action) && !mSettingsLogList.isEmpty()) { + final Intent intent = new Intent(); + intent.setPackage(context.getString(R.string + .config_settingsintelligence_package_name)); + intent.setAction(action); + intent.putExtra(LOG, serialize(mSettingsLogList)); + context.sendBroadcastAsUser(intent, UserHandle.CURRENT); + mSettingsLogList.clear(); + } + }; +} diff --git a/src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java b/src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java index 93a5163645e..ec057572053 100644 --- a/src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java +++ b/src/com/android/settings/core/instrumentation/SettingsMetricsFeatureProvider.java @@ -16,6 +16,8 @@ package com.android.settings.core.instrumentation; +import android.content.Context; + import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider { @@ -23,5 +25,6 @@ public class SettingsMetricsFeatureProvider extends MetricsFeatureProvider { protected void installLogWriters() { super.installLogWriters(); mLoggerWriters.add(new StatsLogWriter()); + mLoggerWriters.add(new SettingsIntelligenceLogWriter()); } } diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk index b0733f4ded7..727da064a56 100644 --- a/tests/robotests/Android.mk +++ b/tests/robotests/Android.mk @@ -49,6 +49,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ guava \ jsr305 \ settings-contextual-card-protos-lite \ + settings-log-bridge-protos-lite \ contextualcards \ settings-logtags \ zxing-core-1.7 diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriterTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriterTest.java new file mode 100644 index 00000000000..30a25945d2f --- /dev/null +++ b/tests/robotests/src/com/android/settings/core/instrumentation/SettingsIntelligenceLogWriterTest.java @@ -0,0 +1,78 @@ +/* + * 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.core.instrumentation; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.intelligence.LogProto.SettingsLog; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SettingsIntelligenceLogWriterTest { + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + } + + @Test + public void serialize_hasSizeOne_returnCorrectData() throws IOException { + final SettingsLog event = SettingsLog.newBuilder() + .setAttribution(SettingsEnums.DASHBOARD_SUMMARY) + .setAction(SettingsEnums.ACTION_SET_NEW_PASSWORD) + .setPageId(SettingsEnums.SET_NEW_PASSWORD_ACTIVITY) + .setChangedPreferenceKey("package") + .setChangedPreferenceIntValue(100) + .build(); + List events = new ArrayList<>(); + events.add(event); + + // execute + final byte[] data = SettingsIntelligenceLogWriter.serialize(events); + + // parse data + final ByteArrayInputStream bin = new ByteArrayInputStream(data); + final DataInputStream inputStream = new DataInputStream(bin); + final int size = inputStream.readInt(); + final byte[] change = new byte[inputStream.readInt()]; + inputStream.read(change); + inputStream.close(); + final SettingsLog settingsLog = SettingsLog.parseFrom(change); + + // assert + assertThat(events.size()).isEqualTo(size); + assertThat(settingsLog.getAttribution()).isEqualTo(SettingsEnums.DASHBOARD_SUMMARY); + assertThat(settingsLog.getAction()).isEqualTo(SettingsEnums.ACTION_SET_NEW_PASSWORD); + assertThat(settingsLog.getPageId()).isEqualTo(SettingsEnums.SET_NEW_PASSWORD_ACTIVITY); + assertThat(settingsLog.getChangedPreferenceKey()).isEqualTo("package"); + assertThat(settingsLog.getChangedPreferenceIntValue()).isEqualTo(100); + } +} From 9703d79ce26c822e0100f9e7d20ab85f6ac6982e Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Fri, 22 Mar 2019 18:10:00 -0700 Subject: [PATCH 10/27] Normalize the icons in Accessibility The icons displayed in Accessibility are larger than others, they should be the same size as app icons. Change the drawable's icon size to fix it. Fixes: 123411506 Test: visual, rebuild Change-Id: Ib693dd2ac8e2e191e5ecb2d0d8d4fdac5a32a2de --- res/drawable/ic_color_inversion.xml | 4 ++-- res/drawable/ic_daltonizer.xml | 4 ++-- .../android/settings/accessibility/AccessibilitySettings.java | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/res/drawable/ic_color_inversion.xml b/res/drawable/ic_color_inversion.xml index aa59f5a0c4d..5d20d0544b7 100644 --- a/res/drawable/ic_color_inversion.xml +++ b/res/drawable/ic_color_inversion.xml @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. --> - - Date: Tue, 26 Mar 2019 16:28:36 -0700 Subject: [PATCH 11/27] Update CodeInspectionTest Update the method to check it contains valid constructor, otherwise report error. Fixes: 129346285 Test: RunSettingsRoboTests Change-Id: I4c5a62b1046e791f3988cd5215879435a831d19d --- ...her_invalid_base_preference_controller_constructor | 3 --- .../BasePreferenceControllerSignatureInspector.java | 11 ++++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor index a72384291b0..675108d94ed 100644 --- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor +++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor @@ -10,8 +10,5 @@ com.android.settings.datausage.WifiDataUsageSummaryPreferenceController com.android.settings.fuelgauge.RestrictAppPreferenceController com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController -com.android.settings.gestures.SystemNavigationEdgeToEdgePreferenceController -com.android.settings.gestures.SystemNavigationLegacyPreferenceController -com.android.settings.gestures.SystemNavigationSwipeUpPreferenceController com.android.settings.security.VisiblePatternProfilePreferenceController com.android.settings.wifi.details.WifiMeteredPreferenceController \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java index 28e394f94cb..2d1dc5c65ea 100644 --- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java +++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerSignatureInspector.java @@ -58,12 +58,17 @@ public class BasePreferenceControllerSignatureInspector extends CodeInspector { if (constructors == null || constructors.length == 0) { badClasses.append(c.getName()).append(","); } + + boolean hasValidConstructor = false; for (Constructor constructor : constructors) { - if (!hasValidConstructorSignature(constructor)) { - badClasses.append(className).append(","); - continue; + if (hasValidConstructorSignature(constructor)) { + hasValidConstructor = true; + break; } } + if (!hasValidConstructor) { + badClasses.append(className).append(","); + } } assertWithMessage("All BasePreferenceController (and subclasses) constructor must either" From 281cf17d899f3353bece0b710d725d171b51861d Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 22 Mar 2019 14:43:19 -0700 Subject: [PATCH 12/27] Skip lifecycle if controller is not available Fixes: 129076378 Test: RunSettingsRoboTests Change-Id: If1e49785c2378ec2cf0f7d2837bc7d4c0ff915a0 --- ...ancedBluetoothDetailsHeaderController.java | 8 +++++ ...dBluetoothDetailsHeaderControllerTest.java | 33 +++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 2c4db1f486a..1d2d793496a 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -62,6 +62,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @VisibleForTesting final Map mIconCache; private CachedBluetoothDevice mCachedDevice; + private BluetoothDevice mBluetoothDevice; @VisibleForTesting BluetoothAdapter mBluetoothAdapter; @VisibleForTesting @@ -102,6 +103,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @Override public void onStart() { + if (!isAvailable()) { + return; + } mCachedDevice.registerCallback(this::onDeviceAttributesChanged); mBluetoothAdapter.registerMetadataListener(mCachedDevice.getDevice(), mMetadataListener, mHandler); @@ -109,6 +113,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @Override public void onStop() { + if (!isAvailable()) { + return; + } mCachedDevice.unregisterCallback(this::onDeviceAttributesChanged); mBluetoothAdapter.unregisterMetadataListener(mCachedDevice.getDevice()); @@ -123,6 +130,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont public void init(CachedBluetoothDevice cachedBluetoothDevice) { mCachedDevice = cachedBluetoothDevice; + mBluetoothDevice = mCachedDevice.getDevice(); } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index 55381556eee..b9616697099 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -18,6 +18,7 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -78,6 +79,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{ mContext = RuntimeEnvironment.application; mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key"); + when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); mController.init(mCachedDevice); mLayoutPreference = new LayoutPreference(mContext, LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null)); @@ -166,7 +168,10 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{ } @Test - public void onStart_registerCallback() { + public void onStart_isAvailable_registerCallback() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) + .thenReturn("true"); + mController.onStart(); verify(mBluetoothAdapter).registerMetadataListener(mBluetoothDevice, @@ -174,12 +179,36 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{ } @Test - public void onStop_unregisterCallback() { + public void onStop_isAvailable_unregisterCallback() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) + .thenReturn("true"); + mController.onStop(); verify(mBluetoothAdapter).unregisterMetadataListener(mBluetoothDevice); } + @Test + public void onStart_notAvailable_registerCallback() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) + .thenReturn("false"); + + mController.onStart(); + + verify(mBluetoothAdapter, never()).registerMetadataListener(mBluetoothDevice, + mController.mMetadataListener, mController.mHandler); + } + + @Test + public void onStop_notAvailable_unregisterCallback() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) + .thenReturn("false"); + + mController.onStop(); + + verify(mBluetoothAdapter, never()).unregisterMetadataListener(mBluetoothDevice); + } + private void assertBatteryLevel(LinearLayout linearLayout, int batteryLevel) { final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary); assertThat(textView.getText().toString()).isEqualTo( From afd71d1689ddbcd1c79eb5ec9709f81e71c4af60 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 21 Mar 2019 12:49:38 -0700 Subject: [PATCH 13/27] Add heap dump options to Developer options. 1. Add a button to capture a system heap dump on demand. 2. Add a toggle to enable/disable automatic system heap dump captures. Bug: 77490269 Test: manual Change-Id: I12b41de82f641ae239ea8e48f0180392aca5dbe8 --- res/xml/development_settings.xml | 9 ++ ...temServerHeapDumpPreferenceController.java | 84 +++++++++++++++++++ .../DevelopmentSettingsDashboardFragment.java | 2 + ...temServerHeapDumpPreferenceController.java | 84 +++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java create mode 100644 src/com/android/settings/development/SystemServerHeapDumpPreferenceController.java diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index a9af2d4e29c..908529ef9a9 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -35,6 +35,10 @@ android:title="@*android:string/bugreport_title" android:dialogTitle="@*android:string/bugreport_title" /> + + + + diff --git a/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java b/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java new file mode 100644 index 00000000000..aa76bb8f14a --- /dev/null +++ b/src/com/android/settings/development/AutomaticSystemServerHeapDumpPreferenceController.java @@ -0,0 +1,84 @@ +/* + * 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.development; + +import android.content.Context; +import android.os.Build; +import android.os.UserManager; +import android.provider.Settings; + +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class AutomaticSystemServerHeapDumpPreferenceController extends + DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener, + PreferenceControllerMixin { + + private static final String KEY_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS = + "automatic_system_server_heap_dumps"; + + private static final int SETTING_VALUE_OFF = 0; + private static final int SETTING_VALUE_ON = 1; + + private final UserManager mUserManager; + private final boolean mIsConfigEnabled; + + public AutomaticSystemServerHeapDumpPreferenceController(Context context) { + super(context); + mIsConfigEnabled = context.getResources().getBoolean( + com.android.internal.R.bool.config_debugEnableAutomaticSystemServerHeapDumps); + mUserManager = context.getSystemService(UserManager.class); + } + + @Override + public boolean isAvailable() { + return Build.IS_DEBUGGABLE && mIsConfigEnabled + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES); + } + + @Override + public String getPreferenceKey() { + return KEY_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, + isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + return true; + } + + @Override + public void updateState(Preference preference) { + final int mode = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, SETTING_VALUE_ON); + ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, SETTING_VALUE_OFF); + ((SwitchPreference) mPreference).setChecked(false); + } +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index adc23a811ab..e9fc7596d44 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -404,6 +404,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra final List controllers = new ArrayList<>(); controllers.add(new MemoryUsagePreferenceController(context)); controllers.add(new BugReportPreferenceController(context)); + controllers.add(new SystemServerHeapDumpPreferenceController(context)); controllers.add(new LocalBackupPasswordPreferenceController(context)); controllers.add(new StayAwakePreferenceController(context, lifecycle)); controllers.add(new HdcpCheckingPreferenceController(context)); @@ -418,6 +419,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new ClearAdbKeysPreferenceController(context, fragment)); controllers.add(new LocalTerminalPreferenceController(context)); controllers.add(new BugReportInPowerPreferenceController(context)); + controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context)); controllers.add(new MockLocationAppPreferenceController(context, fragment)); controllers.add(new DebugViewAttributesPreferenceController(context)); controllers.add(new SelectDebugAppPreferenceController(context, fragment)); diff --git a/src/com/android/settings/development/SystemServerHeapDumpPreferenceController.java b/src/com/android/settings/development/SystemServerHeapDumpPreferenceController.java new file mode 100644 index 00000000000..599162e13f4 --- /dev/null +++ b/src/com/android/settings/development/SystemServerHeapDumpPreferenceController.java @@ -0,0 +1,84 @@ +/* + * 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.development; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Build; +import android.os.Handler; +import android.os.RemoteException; +import android.os.UserManager; +import android.util.Log; +import android.widget.Toast; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class SystemServerHeapDumpPreferenceController extends DeveloperOptionsPreferenceController + implements PreferenceControllerMixin { + + private static final String KEY_SYSTEM_SERVER_HEAP_DUMP = "system_server_heap_dump"; + + /** How long to keep the preference disabled before re-enabling. */ + private static final long ENABLE_TIMEOUT_MILLIS = 5000L; + + private final UserManager mUserManager; + + private Handler mHandler; + + public SystemServerHeapDumpPreferenceController(Context context) { + super(context); + + mUserManager = context.getSystemService(UserManager.class); + mHandler = new Handler(); + } + + @Override + public boolean isAvailable() { + return Build.IS_DEBUGGABLE + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES); + } + + @Override + public String getPreferenceKey() { + return KEY_SYSTEM_SERVER_HEAP_DUMP; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!KEY_SYSTEM_SERVER_HEAP_DUMP.equals(preference.getKey())) { + return false; + } + try { + // Temporarily disable the preference so the user doesn't start two dumps in a row. + preference.setEnabled(false); + Toast.makeText(mContext, R.string.capturing_system_heap_dump_message, + Toast.LENGTH_SHORT).show(); + ActivityManager.getService().requestSystemServerHeapDump(); + mHandler.postDelayed(() -> preference.setEnabled(true), ENABLE_TIMEOUT_MILLIS); + return true; + } catch (RemoteException e) { + Log.e(TAG, "error taking system heap dump", e); + Toast.makeText(mContext, R.string.error_capturing_system_heap_dump_message, + Toast.LENGTH_SHORT).show(); + } + return false; + } +} From bd677df0099e09652529f67e9520e1beb4d6a5fe Mon Sep 17 00:00:00 2001 From: Salvador Martinez Date: Tue, 26 Mar 2019 15:15:49 -0700 Subject: [PATCH 14/27] Update icons to use tint and vectors A bunch of icons were using PNGs which didn't allow their color to change when in dark mode. This CL simply adds vector drawables for all the PNGs and sets the tint to the correct color where needed. Test: visual inspection, robotests pass Bug: 126199274 Change-Id: I372d94e374173ab0572dacb03674a40fa14cffb9 --- res/drawable/ic_cellular_1_bar.xml | 29 +++++++++++++++++++ res/drawable/ic_settings_aod.xml | 3 +- res/drawable/ic_settings_camera.xml | 3 +- res/drawable/ic_settings_phone_idle.xml | 25 ++++++++++++++++ res/drawable/ic_settings_voice_calls.xml | 25 ++++++++++++++++ .../settings/fuelgauge/BatteryEntry.java | 6 ++-- 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 res/drawable/ic_cellular_1_bar.xml create mode 100644 res/drawable/ic_settings_phone_idle.xml create mode 100644 res/drawable/ic_settings_voice_calls.xml diff --git a/res/drawable/ic_cellular_1_bar.xml b/res/drawable/ic_cellular_1_bar.xml new file mode 100644 index 00000000000..b3d2624fa27 --- /dev/null +++ b/res/drawable/ic_cellular_1_bar.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/res/drawable/ic_settings_aod.xml b/res/drawable/ic_settings_aod.xml index 8d9134947ee..b238e3a3201 100644 --- a/res/drawable/ic_settings_aod.xml +++ b/res/drawable/ic_settings_aod.xml @@ -18,7 +18,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + android:autoMirrored="true" + android:tint="?android:attr/colorControlNormal"> + + + diff --git a/res/drawable/ic_settings_voice_calls.xml b/res/drawable/ic_settings_voice_calls.xml new file mode 100644 index 00000000000..b455f0ab3d8 --- /dev/null +++ b/res/drawable/ic_settings_voice_calls.xml @@ -0,0 +1,25 @@ + + + + diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index c91cc8ffd2c..38ae2b2df76 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -145,7 +145,7 @@ public class BatteryEntry { break; case CELL: name = context.getResources().getString(R.string.power_cell); - iconId = R.drawable.ic_settings_cell_standby; + iconId = R.drawable.ic_cellular_1_bar; break; case PHONE: name = context.getResources().getString(R.string.power_phone); @@ -199,11 +199,11 @@ public class BatteryEntry { } break; case UNACCOUNTED: name = context.getResources().getString(R.string.power_unaccounted); - iconId = R.drawable.ic_power_system; + iconId = R.drawable.ic_android; break; case OVERCOUNTED: name = context.getResources().getString(R.string.power_overcounted); - iconId = R.drawable.ic_power_system; + iconId = R.drawable.ic_android; break; case CAMERA: name = context.getResources().getString(R.string.power_camera); From 9c2fae83a30dfb1740be8fcd2db60f22b38a4c80 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Tue, 26 Mar 2019 18:50:21 -0700 Subject: [PATCH 15/27] Fix fingerprint icon on pattern screen Test: atest [...]/ChooseLockPatternTest.java Bug: 129342100 Change-Id: I4e670b6265fe4a77ecbd8ede9a703fa0762f7504 --- res/layout-land/choose_lock_pattern.xml | 2 +- res/layout-land/confirm_lock_pattern_normal.xml | 2 +- res/layout-land/fingerprint_enroll_enrolling.xml | 2 +- res/layout-land/fingerprint_enroll_find_sensor.xml | 2 +- res/layout-land/fingerprint_enroll_finish.xml | 2 +- res/layout/choose_lock_pattern_common.xml | 2 +- res/layout/confirm_lock_pattern_normal_base.xml | 2 +- src/com/android/settings/password/ChooseLockPattern.java | 2 +- .../com/android/settings/password/ChooseLockPatternTest.java | 4 +--- 9 files changed, 9 insertions(+), 11 deletions(-) diff --git a/res/layout-land/choose_lock_pattern.xml b/res/layout-land/choose_lock_pattern.xml index 1cc435249c4..5b06f0d8faa 100644 --- a/res/layout-land/choose_lock_pattern.xml +++ b/res/layout-land/choose_lock_pattern.xml @@ -44,7 +44,7 @@ android:orientation="vertical"> Date: Wed, 27 Mar 2019 10:15:45 +0800 Subject: [PATCH 16/27] Tweak summary for privacy entry Test: visual Fixes: 129345927 Change-Id: I9ce4481565c417d375b24c37ed91d507f2ca57cf --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 9b23ca9a1b1..c9d3cb82d69 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10885,7 +10885,7 @@ Privacy - Permissions, web activity, personal data + Permissions, account activity, personal data Remove From ebf7b888d6f32bc2a535d721b196f337c8e5adf0 Mon Sep 17 00:00:00 2001 From: tmfang Date: Wed, 27 Mar 2019 10:53:24 +0800 Subject: [PATCH 17/27] Change icons for disable and enable button Test: visual Fixes: 124116610 Change-Id: Ia300eeba36d853117699d3bec724373df2e354d3 --- res/drawable/ic_settings_disable.xml | 25 +++++++++++++++++++ res/drawable/ic_settings_enable.xml | 25 +++++++++++++++++++ .../AppButtonsPreferenceController.java | 6 ++--- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 res/drawable/ic_settings_disable.xml create mode 100644 res/drawable/ic_settings_enable.xml diff --git a/res/drawable/ic_settings_disable.xml b/res/drawable/ic_settings_disable.xml new file mode 100644 index 00000000000..d0edf4a4bd3 --- /dev/null +++ b/res/drawable/ic_settings_disable.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/res/drawable/ic_settings_enable.xml b/res/drawable/ic_settings_enable.xml new file mode 100644 index 00000000000..560daef774a --- /dev/null +++ b/res/drawable/ic_settings_enable.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java index e118ed609d2..d9ddc2fc769 100644 --- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java @@ -560,15 +560,15 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp || isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) { // Disable button for core system applications. mButtonsPref.setButton2Text(R.string.disable_text) - .setButton2Icon(R.drawable.ic_settings_delete); + .setButton2Icon(R.drawable.ic_settings_disable); } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { mButtonsPref.setButton2Text(R.string.disable_text) - .setButton2Icon(R.drawable.ic_settings_delete); + .setButton2Icon(R.drawable.ic_settings_disable); disableable = !mApplicationFeatureProvider.getKeepEnabledPackages() .contains(mAppEntry.info.packageName); } else { mButtonsPref.setButton2Text(R.string.enable_text) - .setButton2Icon(R.drawable.ic_settings_install); + .setButton2Icon(R.drawable.ic_settings_enable); disableable = true; } From 5bf939e934ad71f5dba1267a5307e7fbde32592e Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Tue, 26 Mar 2019 17:16:27 +0800 Subject: [PATCH 18/27] Add interface for homepapge swipe to dismiss. - Create an ItemTouchHelper. - Attach the helper to the recycler view. - Pass change to the adapter to handle the swipe. Bug: 126214056 Test: rebuild Change-Id: I312ee1357158db84f9ee328be4722fe067984604 --- .../ContextualCardsAdapter.java | 8 ++- .../ContextualCardsFragment.java | 11 +++- .../slices/SwipeDismissalDelegate.java | 66 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegate.java diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java index 241834ddd13..d6df380239a 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java @@ -28,6 +28,7 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer; +import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate.DismissalItemTouchHelperListener; import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer; import java.util.ArrayList; @@ -35,7 +36,7 @@ import java.util.List; import java.util.Map; public class ContextualCardsAdapter extends RecyclerView.Adapter - implements ContextualCardUpdateListener { + implements ContextualCardUpdateListener, DismissalItemTouchHelperListener { static final int SPAN_COUNT = 2; private static final String TAG = "ContextualCardsAdapter"; @@ -136,4 +137,9 @@ public class ContextualCardsAdapter extends RecyclerView.Adapter Date: Fri, 22 Mar 2019 23:59:25 +0800 Subject: [PATCH 19/27] Provide connect button in Wi-Fi detail page User can click connect button to connect this AP at the detail page even the Wi-Fi disabled. Would turn on Wi-Fi if disabled and then connect to this saved network. Bug: 126503889 Test: atest and manual test Change-Id: I313e290474d6623a6c3e84027e81d0d1ebf16919 --- .../WifiDetailPreferenceController.java | 297 ++++++++++++++++-- 1 file changed, 272 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java index 5fc350bbc1d..955525dd859 100644 --- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -41,6 +41,7 @@ import android.net.RouteInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.CountDownTimer; import android.os.Handler; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -84,6 +85,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; +import java.time.Duration; import java.util.StringJoiner; import java.util.stream.Collectors; @@ -127,6 +129,19 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController @VisibleForTesting static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses"; + private static final int STATE_NONE = 1; + private static final int STATE_ENABLE_WIFI = 2; + private static final int STATE_ENABLE_WIFI_FAILED = 3; + private static final int STATE_CONNECTING = 4; + private static final int STATE_CONNECTED = 5; + private static final int STATE_FAILED = 6; + private static final int STATE_NOT_IN_RANGE = 7; + private static final int STATE_DISCONNECTED = 8; + private static final long TIMEOUT = Duration.ofSeconds(10).toMillis(); + + // Be static to avoid too much object not be reset. + private static CountDownTimer mTimer; + private AccessPoint mAccessPoint; private final ConnectivityManager mConnectivityManager; private final Fragment mFragment; @@ -143,6 +158,9 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private final WifiTracker mWifiTracker; private final MetricsFeatureProvider mMetricsFeatureProvider; private boolean mIsOutOfRange; + private boolean mConnected; + private int mConnectingState; + private WifiManager.ActionListener mConnectListener; // UI elements - in order of appearance private ActionButtonsPreference mButtonsPref; @@ -182,7 +200,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController // fall through case WifiManager.NETWORK_STATE_CHANGED_ACTION: case WifiManager.RSSI_CHANGED_ACTION: - updateNetworkInfo(); + refreshPage(); break; } } @@ -197,7 +215,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController public void onLinkPropertiesChanged(Network network, LinkProperties lp) { if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) { mLinkProperties = lp; - updateIpLayerInfo(); + refreshIpLayerInfo(); } } @@ -224,12 +242,17 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } mNetworkCapabilities = nc; refreshButtons(); - updateIpLayerInfo(); + refreshIpLayerInfo(); } } @Override public void onLost(Network network) { + // If support detail page for saved network, should update as disconnect but not exit. + if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) { + return; + } + if (network.equals(mNetwork)) { exitActivity(); } @@ -240,13 +263,23 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController /** Called when the state of Wifi has changed. */ public void onWifiStateChanged(int state) { Log.d(TAG, "onWifiStateChanged(" + state + ")"); - // Do nothing. + if (mConnectingState == STATE_ENABLE_WIFI && state == WifiManager.WIFI_STATE_ENABLED) { + updateConnectingState(STATE_CONNECTING); + } } /** Called when the connection state of wifi has changed. */ public void onConnectedChanged() { - Log.d(TAG, "onConnectedChanged"); - // Do nothing. + updateAccessPointFromScannedList(); + if (mConnected != mAccessPoint.isActive()) { + Log.d(TAG, "Connection state changed!"); + mConnected = mAccessPoint.isActive(); + if (mAccessPoint.isActive()) { + updateConnectingState(STATE_CONNECTED); + } else { + updateConnectingState(STATE_DISCONNECTED); + } + } } /** @@ -254,8 +287,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController * {@link WifiTracker#getAccessPoints()} should be called to get the updated list. */ public void onAccessPointsChanged() { - Log.d(TAG, "onAccessPointsChanged"); - updateNetworkInfo(); + refreshPage(); } }; @@ -314,6 +346,19 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } else { mWifiTracker = null; } + mConnected = mAccessPoint.isActive(); + mConnectingState = STATE_NONE; + mConnectListener = new WifiManager.ActionListener() { + @Override + public void onSuccess() { + // Do nothing + } + + @Override + public void onFailure(int reason) { + updateConnectingState(STATE_FAILED); + } + }; } @Override @@ -339,10 +384,14 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController .setButton1OnClickListener(view -> forgetNetwork()) .setButton2Text(R.string.wifi_sign_in_button_text) .setButton2OnClickListener(view -> signIntoNetwork()) - .setButton3Text(R.string.share) - .setButton3Icon(R.drawable.ic_qrcode_24dp) - .setButton3OnClickListener(view -> shareNetwork()) - .setButton3Visible(WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mAccessPoint)); + .setButton3Text(R.string.wifi_connect) + .setButton3Icon(R.drawable.ic_settings_wireless) + .setButton3OnClickListener(view -> connectNetwork()) + .setButton3Enabled(true) + .setButton4Text(R.string.share) + .setButton4Icon(R.drawable.ic_qrcode_24dp) + .setButton4OnClickListener(view -> shareNetwork()) + .setButton4Visible(WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mAccessPoint)); mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED); @@ -397,14 +446,18 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } } + private void updateNetworkInfo() { + mNetwork = mWifiManager.getCurrentNetwork(); + mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); + mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); + } + @Override public void onResume() { // Ensure mNetwork is set before any callbacks above are delivered, since our // NetworkCallback only looks at changes to mNetwork. - mNetwork = mWifiManager.getCurrentNetwork(); - mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork); - mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork); updateNetworkInfo(); + refreshPage(); mContext.registerReceiver(mReceiver, mFilter); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback, mHandler); @@ -421,11 +474,13 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); } - private void updateNetworkInfo() { + private void refreshPage() { if(!updateAccessPoint()) { return; } + Log.d(TAG, "Update UI!"); + // refresh header refreshEntityHeader(); @@ -441,26 +496,37 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController // Receive Link Speed Pref refreshRxSpeed(); // IP related information - updateIpLayerInfo(); + refreshIpLayerInfo(); // MAC Address Pref refreshMacAddress(); - } private boolean updateAccessPoint() { boolean changed = false; if (mWifiTracker != null) { + // remember mIsOutOfRange as old before updated + boolean oldState = mIsOutOfRange; updateAccessPointFromScannedList(); // refresh UI if signal level changed for disconnect network. changed = mRssiSignalLevel != mAccessPoint.getLevel(); + changed |= oldState != mIsOutOfRange; } if (mAccessPoint.isActive()) { - // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the - // callbacks. mNetwork doesn't change except in onResume. + // Sometimes {@link WifiManager#getCurrentNetwork()} return null after connected, + // refresh it if needed. + if (mNetwork == null) { + updateNetworkInfo(); + } mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork); mWifiInfo = mWifiManager.getConnectionInfo(); if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) { + // Once connected, can't get mNetworkInfo immediately, return false and wait for + // next time to update UI. + if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) { + return false; + } + exitActivity(); return false; } @@ -474,6 +540,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController } private void updateAccessPointFromScannedList() { + if (mWifiTracker == null) return; + mIsOutOfRange = true; if (mAccessPoint.getConfig() == null) { @@ -484,7 +552,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController if (ap.getConfig() != null && mAccessPoint.matches(ap.getConfig())) { mAccessPoint = ap; - mIsOutOfRange = false; + mIsOutOfRange = !mAccessPoint.isReachable(); return; } } @@ -543,6 +611,11 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController band = mContext.getResources().getString(R.string.wifi_band_5ghz); } else { Log.e(TAG, "Unexpected frequency " + frequency); + // Connecting state is unstable, make it disappeared if unexpected + if (mConnectingState == STATE_CONNECTING) { + mFrequencyPref.setVisible(false); + } + return; } mFrequencyPref.setSummary(band); mFrequencyPref.setVisible(true); @@ -616,12 +689,21 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private void refreshButtons() { mButtonsPref.setButton1Visible(canForgetNetwork()); mButtonsPref.setButton2Visible(canSignIntoNetwork()); - mButtonsPref.setButton3Visible(canShareNetwork()); - mButtonsPref.setVisible( - canSignIntoNetwork() || canForgetNetwork() || canShareNetwork()); + mButtonsPref.setButton3Visible(canConnectNetwork()); + mButtonsPref.setButton4Visible(canShareNetwork()); + mButtonsPref.setVisible(canSignIntoNetwork() + || canForgetNetwork() + || canShareNetwork() + || canConnectNetwork()); } - private void updateIpLayerInfo() { + private boolean canConnectNetwork() { + // Display connect button for disconnected AP even not in the range. + return SavedAccessPointsWifiSettings.usingDetailsFragment(mContext) + && !mAccessPoint.isActive(); + } + + private void refreshIpLayerInfo() { // Hide IP layer info if not a connected network. if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) { mIpAddressPref.setVisible(false); @@ -823,4 +905,169 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController private boolean usingDataUsageHeader(Context context) { return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER); } + + private void connectNetwork() { + final Activity activity = mFragment.getActivity(); + // error handling, connected/saved network should have mWifiConfig. + if (mWifiConfig == null) { + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + return; + } + + // init state before connect + mConnectingState = STATE_NONE; + + if (mWifiManager.isWifiEnabled()) { + updateConnectingState(STATE_CONNECTING); + } else { + // Enable Wi-Fi automatically to connect AP + updateConnectingState(STATE_ENABLE_WIFI); + } + } + + private void updateConnectingState(int state) { + final Activity activity = mFragment.getActivity(); + Log.d(TAG, "updateConnectingState from " + mConnectingState + " to " + state); + switch (mConnectingState) { + case STATE_NONE: + case STATE_ENABLE_WIFI: + if (state == STATE_ENABLE_WIFI) { + Log.d(TAG, "Turn on Wi-Fi automatically!"); + updateConnectedButton(STATE_ENABLE_WIFI); + Toast.makeText(activity, + R.string.wifi_turned_on_message, + Toast.LENGTH_SHORT).show(); + mWifiManager.setWifiEnabled(true); + // start timer for error handling + startTimer(); + } else if (state == STATE_CONNECTING) { + Log.d(TAG, "connecting..."); + updateConnectedButton(STATE_CONNECTING); + mWifiManager.connect(mWifiConfig.networkId, mConnectListener); + // start timer for error handling since framework didn't call back if failed + startTimer(); + } else if (state == STATE_ENABLE_WIFI_FAILED) { + Log.e(TAG, "Wi-Fi failed to enable network!"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_ENABLE_WIFI_FAILED); + } + // Do not break here for disconnected event. + case STATE_CONNECTED: + if (state == STATE_DISCONNECTED) { + Log.d(TAG, "disconnected"); + // reset state + state = STATE_NONE; + updateConnectedButton(STATE_DISCONNECTED); + refreshPage(); + // clear for getting MAC Address from saved configuration + mWifiInfo = null; + } + break; + case STATE_CONNECTING: + if (state == STATE_CONNECTED) { + Log.d(TAG, "connected"); + stopTimer(); + updateConnectedButton(STATE_CONNECTED); + Toast.makeText(activity, + mContext.getString(R.string.wifi_connected_to_message, + mAccessPoint.getTitle()), + Toast.LENGTH_SHORT).show(); + + updateNetworkInfo(); + refreshPage(); + } else if (state == STATE_NOT_IN_RANGE) { + Log.d(TAG, "AP not in range"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_not_in_range_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_NOT_IN_RANGE); + } else if (state == STATE_FAILED) { + Log.d(TAG, "failed"); + stopTimer(); + // reset state + state = STATE_NONE; + Toast.makeText(activity, + R.string.wifi_failed_connect_message, + Toast.LENGTH_SHORT).show(); + updateConnectedButton(STATE_FAILED); + } + break; + default: + Log.e(TAG, "Invalid state : " + mConnectingState); + // don't update invalid state + return; + } + + mConnectingState = state; + } + + private void updateConnectedButton(int state) { + switch (state) { + case STATE_ENABLE_WIFI: + case STATE_CONNECTING: + mButtonsPref.setButton3Text(R.string.wifi_connecting) + .setButton3Enabled(false); + break; + case STATE_CONNECTED: + mButtonsPref.setButton3Visible(false); + break; + case STATE_DISCONNECTED: + case STATE_NOT_IN_RANGE: + case STATE_FAILED: + case STATE_ENABLE_WIFI_FAILED: + mButtonsPref.setButton3Text(R.string.wifi_connect) + .setButton3Icon(R.drawable.ic_settings_wireless) + .setButton3Enabled(true) + .setButton3Visible(true); + break; + default: + Log.e(TAG, "Invalid connect button state : " + state); + break; + } + } + + private void startTimer() { + if (mTimer != null) { + stopTimer(); + } + + mTimer = new CountDownTimer(TIMEOUT, TIMEOUT + 1) { + @Override + public void onTick(long millisUntilFinished) { + // Do nothing + } + @Override + public void onFinish() { + Log.e(TAG, "Timeout for state:" + mConnectingState); + if (mConnectingState == STATE_ENABLE_WIFI) { + updateConnectingState(STATE_ENABLE_WIFI_FAILED); + } else if (mConnectingState == STATE_CONNECTING) { + updateAccessPointFromScannedList(); + if (mIsOutOfRange) { + updateConnectingState(STATE_NOT_IN_RANGE); + } else { + updateConnectingState(STATE_FAILED); + } + } + } + }; + mTimer.start(); + } + + private void stopTimer() { + if (mTimer == null) return; + + mTimer.cancel(); + mTimer = null; + } } From c085f18cf1585b394e8f0a4b24fd3f99660a70ac Mon Sep 17 00:00:00 2001 From: clownshen Date: Wed, 27 Mar 2019 15:03:07 +0800 Subject: [PATCH 20/27] Fix broken test in WifiDetailPreferenceControllerTest Bug: 129292549 Test: atest WifiDetailPreferenceControllerTest Change-Id: Idd8764425816b6851efd5cb716bc968e19b06ea0 --- .../WifiDetailPreferenceControllerTest.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java index 947d6187b28..0a565cd2ff8 100644 --- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java @@ -53,6 +53,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import android.provider.Settings; +import android.util.FeatureFlagUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; @@ -77,7 +78,6 @@ import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.wifi.AccessPoint; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; @@ -99,7 +99,6 @@ import java.util.stream.Collectors; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDevicePolicyManager.class, ShadowEntityHeaderController.class}) -@Ignore("b/129292549") public class WifiDetailPreferenceControllerTest { private static final int LEVEL = 1; @@ -274,6 +273,11 @@ public class WifiDetailPreferenceControllerTest { when(mockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable()); setupMockedPreferenceScreen(); + + // Disable saved network detail page feature for this test + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.WIFI_DETAILS_SAVED_SCREEN, false); + when(mockAccessPoint.isActive()).thenReturn(true); + mController = newWifiDetailPreferenceController(); } @@ -948,6 +952,12 @@ public class WifiDetailPreferenceControllerTest { when(pref.setButton3Visible(anyBoolean())).thenReturn(pref); when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + when(pref.setButton4Text(anyInt())).thenReturn(pref); + when(pref.setButton4Icon(anyInt())).thenReturn(pref); + when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref); + when(pref.setButton4Visible(anyBoolean())).thenReturn(pref); + when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref); + return pref; } } From b661549b81e424ff5bf1214241c5d0d443e3f410 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Tue, 26 Mar 2019 18:13:49 +0800 Subject: [PATCH 21/27] List reachable access points on top of the list in WifiNetworkListFragment Bug: 128942314 Test: manual Change-Id: I036bf09c066da3a68d6e2fc4685313ccf16bff03 --- .../wifi/dpp/WifiNetworkListFragment.java | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java index 84f1c363f35..2bf411a5905 100644 --- a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java +++ b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java @@ -34,7 +34,6 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.wifi.AddNetworkFragment; -import com.android.settings.wifi.savedaccesspoints.SavedNetworkComparator; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPointPreference; import com.android.settingslib.wifi.WifiSavedConfigUtils; @@ -43,6 +42,7 @@ import com.android.settingslib.wifi.WifiTrackerFactory; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class WifiNetworkListFragment extends SettingsPreferenceFragment implements WifiTracker.WifiListener, AccessPoint.AccessPointListener { @@ -282,27 +282,38 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen return; } - // TODO(b/128942314): Lists reachable AccessPoints on top of the list - final List savedAccessPoints = + List savedAccessPoints = WifiSavedConfigUtils.getAllConfigs(getContext(), mWifiManager); - Collections.sort(savedAccessPoints, SavedNetworkComparator.INSTANCE); + + savedAccessPoints = savedAccessPoints.stream() + .filter(accessPoint -> isValidForDppConfiguration(accessPoint)) + .map(accessPoint -> getScannedAccessPointIfAvailable(accessPoint)) + .sorted((ap1, ap2) -> { + // orders reachable Wi-Fi networks on top + if (ap1.isReachable() && !ap2.isReachable()) { + return -1; + } else if (!ap1.isReachable() && ap2.isReachable()) { + return 1; + } + + String ap1Title = nullToEmpty(ap1.getTitle()); + String ap2Title = nullToEmpty(ap2.getTitle()); + + return ap1Title.compareToIgnoreCase(ap2Title); + }).collect(Collectors.toList()); int index = 0; mAccessPointsPreferenceCategory.removeAll(); for (AccessPoint savedAccessPoint : savedAccessPoints) { - if (isValidForDppConfiguration(savedAccessPoint)) { - // Replaces with an AccessPoint from scanned result for signal information - savedAccessPoint = getScannedAccessPointIfAvailable(savedAccessPoint); - final AccessPointPreference preference = - createAccessPointPreference(savedAccessPoint); + final AccessPointPreference preference = + createAccessPointPreference(savedAccessPoint); - preference.setOrder(index++); - preference.setEnabled(savedAccessPoint.isReachable()); - savedAccessPoint.setListener(this); + preference.setOrder(index++); + preference.setEnabled(savedAccessPoint.isReachable()); + savedAccessPoint.setListener(this); - preference.refresh(); - mAccessPointsPreferenceCategory.addPreference(preference); - } + preference.refresh(); + mAccessPointsPreferenceCategory.addPreference(preference); } mAddPreference.setOrder(index); mAccessPointsPreferenceCategory.addPreference(mAddPreference); @@ -312,6 +323,11 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen } } + private String nullToEmpty(String string) { + return (string == null) ? "" : string; + } + + // Replaces with an AccessPoint from scanned result for signal information private AccessPoint getScannedAccessPointIfAvailable(AccessPoint savedAccessPoint) { final List scannedAccessPoints = mWifiTracker.getAccessPoints(); final WifiConfiguration savedWifiConfiguration = savedAccessPoint.getConfig(); From 22138167201bbc2e7f376e6b649e97e39923758d Mon Sep 17 00:00:00 2001 From: cosmohsieh Date: Wed, 27 Mar 2019 17:32:46 +0800 Subject: [PATCH 22/27] [Passpointv2] Fix Network detail page title Sets original detail title if the chart header feautre on detail page don't enable. Bug: 128569047 Test: manual Change-Id: I43a7b1bd11294b34b39b46e0d8f63a844af1585e --- src/com/android/settings/wifi/WifiSettings.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 585cf71ac95..91672104e45 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.provider.Settings; +import android.util.FeatureFlagUtils; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -57,6 +58,7 @@ import com.android.settings.LinkifyUtils; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.SettingsActivity; +import com.android.settings.core.FeatureFlags; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.location.ScanningSettings; @@ -960,8 +962,10 @@ public class WifiSettings extends RestrictedSettingsFragment private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) { final AccessPoint accessPoint = pref.getAccessPoint(); final Context context = getContext(); - final CharSequence title = SavedAccessPointsWifiSettings.usingDetailsFragment(context) ? - accessPoint.getTitle() : context.getText(R.string.pref_title_network_details); + final CharSequence title = + FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER) + ? accessPoint.getTitle() + : context.getText(R.string.pref_title_network_details); new SubSettingLauncher(getContext()) .setTitleText(title) From 94d9790fc965ca4ba89c731561ccd7ef3f1e8a8e Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Tue, 26 Mar 2019 09:50:22 -0700 Subject: [PATCH 23/27] Add an intent for launching mobile network list In various cases when a user has rebooted their device to enable DSDS mode, the eSIM manager app needs to be able to post a notification that on tapping will lead the user to the list of configured SIMs (mobile networks) so that they can they manage things like which one is the default for calls, SMS, and data. Bug: 129281152 Test: 'adb shell am start -a android.settings.MOBILE_NETWORK_LIST' should launch the list of mobile networks available on the device Change-Id: Ie94d937c803fb5ade09084f2c994cfff888d8aa7 --- AndroidManifest.xml | 13 +++++++++++++ src/com/android/settings/Settings.java | 1 + .../settings/core/gateway/SettingsGateway.java | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4166d6f0624..28de61adc7a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -196,6 +196,19 @@ android:value="true" /> + + + + + + + + + Date: Tue, 26 Mar 2019 13:55:44 -0700 Subject: [PATCH 24/27] Check wfc roaming config when read Bug: 127913488 Test: Build Change-Id: I0a3e8d3f4bc0590f12085f5963892a1d8278efdc --- .../WifiCallingPreferenceController.java | 23 ++++++++- .../WifiCallingPreferenceControllerTest.java | 47 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java index dfa12715f77..96521ea24ac 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java @@ -21,8 +21,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Looper; +import android.os.PersistableBundle; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -50,17 +52,24 @@ public class WifiCallingPreferenceController extends TelephonyBasePreferenceCont private TelephonyManager mTelephonyManager; @VisibleForTesting + CarrierConfigManager mCarrierConfigManager; + @VisibleForTesting ImsManager mImsManager; @VisibleForTesting PhoneAccountHandle mSimCallManager; private PhoneCallStateListener mPhoneStateListener; private Preference mPreference; + private boolean mEditableWfcRoamingMode; + private boolean mUseWfcHomeModeForRoaming; public WifiCallingPreferenceController(Context context, String key) { super(context, key); + mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); mTelephonyManager = context.getSystemService(TelephonyManager.class); mSimCallManager = context.getSystemService(TelecomManager.class).getSimCallManager(); mPhoneStateListener = new PhoneCallStateListener(Looper.getMainLooper()); + mEditableWfcRoamingMode = true; + mUseWfcHomeModeForRoaming = false; } @Override @@ -109,9 +118,9 @@ public class WifiCallingPreferenceController extends TelephonyBasePreferenceCont } else { int resId = com.android.internal.R.string.wifi_calling_off_summary; if (mImsManager.isWfcEnabledByUser()) { + boolean wfcRoamingEnabled = mEditableWfcRoamingMode && !mUseWfcHomeModeForRoaming; final boolean isRoaming = mTelephonyManager.isNetworkRoaming(); - int wfcMode = mImsManager.getWfcMode(isRoaming); - + int wfcMode = mImsManager.getWfcMode(isRoaming && wfcRoamingEnabled); switch (wfcMode) { case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY: resId = com.android.internal.R.string.wfc_mode_wifi_only_summary; @@ -137,6 +146,16 @@ public class WifiCallingPreferenceController extends TelephonyBasePreferenceCont mSubId = subId; mTelephonyManager = TelephonyManager.from(mContext).createForSubscriptionId(mSubId); mImsManager = ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(mSubId)); + if (mCarrierConfigManager != null) { + final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + if (carrierConfig != null) { + mEditableWfcRoamingMode = carrierConfig.getBoolean( + CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL); + mUseWfcHomeModeForRoaming = carrierConfig.getBoolean( + CarrierConfigManager + .KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL); + } + } } private class PhoneCallStateListener extends PhoneStateListener { diff --git a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java index c0b1dab0efd..9eb62f6ee9a 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java @@ -18,11 +18,14 @@ package com.android.settings.network.telephony; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -32,6 +35,7 @@ import androidx.preference.PreferenceScreen; import com.android.ims.ImsConfig; import com.android.ims.ImsManager; +import com.android.internal.R; import com.android.settings.core.BasePreferenceController; import org.junit.Before; @@ -46,6 +50,8 @@ import org.robolectric.RuntimeEnvironment; public class WifiCallingPreferenceControllerTest { private static final int SUB_ID = 2; + @Mock + private CarrierConfigManager mCarrierConfigManager; @Mock private TelephonyManager mTelephonyManager; @Mock @@ -57,6 +63,7 @@ public class WifiCallingPreferenceControllerTest { private Preference mPreference; private PreferenceCategory mPreferenceCategory; private Context mContext; + private PersistableBundle mCarrierConfig; @Before public void setUp() { @@ -69,10 +76,14 @@ public class WifiCallingPreferenceControllerTest { mPreference = new Preference(mContext); mController = new WifiCallingPreferenceController(mContext, "wifi_calling"); + mController.mCarrierConfigManager = mCarrierConfigManager; mController.init(SUB_ID); mController.mImsManager = mImsManager; mPreference.setKey(mController.getPreferenceKey()); + mCarrierConfig = new PersistableBundle(); + when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfig); + mPreferenceCategory = new PreferenceCategory(mContext); when(mPreferenceScreen.findPreference( WifiCallingPreferenceController.KEY_PREFERENCE_CATEGORY)).thenReturn( @@ -102,6 +113,42 @@ public class WifiCallingPreferenceControllerTest { assertThat(mPreference.isEnabled()).isFalse(); } + @Test + public void updateState_wfcNonRoaming() { + assertNull(mController.mSimCallManager); + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false); + // update the config value by calling init again. + mController.init(SUB_ID); + mController.mImsManager = mImsManager; + + when(mImsManager.getWfcMode(true)).thenReturn( + ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED); + when(mImsManager.getWfcMode(false)).thenReturn( + ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED); + when(mImsManager.isWfcEnabledByUser()).thenReturn(true); + when(mTelephonyManager.isNetworkRoaming()).thenReturn(true); + + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.wfc_mode_cellular_preferred_summary)); + } + + @Test + public void updateState_wfcRoaming() { + assertNull(mController.mSimCallManager); + + when(mImsManager.getWfcMode(true)).thenReturn( + ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED); + when(mImsManager.getWfcMode(false)).thenReturn( + ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED); + when(mImsManager.isWfcEnabledByUser()).thenReturn(true); + when(mTelephonyManager.isNetworkRoaming()).thenReturn(true); + + mController.updateState(mPreference); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.wfc_mode_wifi_preferred_summary)); + } + @Test public void displayPreference_notAvailable_setCategoryInvisible() { mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID); From d646ad7778b9eb425c20e34db13370542ef932eb Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 26 Mar 2019 16:20:33 -0700 Subject: [PATCH 25/27] exit app info page if app is a hidden system module. Fixes: 120546598 Test: robotest Change-Id: I763ce9d65904e2b0c2a6144776c42881a1269c52 --- .../appinfo/AppInfoDashboardFragment.java | 24 +++++++- .../appinfo/AppInfoDashboardFragmentTest.java | 55 ++++++++++++++++++- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 4987286ed36..183d3843447 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -187,6 +187,9 @@ public class AppInfoDashboardFragment extends DashboardFragment if (!ensurePackageInfoAvailable(activity)) { return; } + if (!ensureDisplayableModule(activity)) { + return; + } startListeningToPackageRemove(); setHasOptionsMenu(true); @@ -260,8 +263,8 @@ public class AppInfoDashboardFragment extends DashboardFragment new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); controllers.add(mInstantAppButtonPreferenceController); mAppButtonsPreferenceController = new AppButtonsPreferenceController( - (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, - REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); + (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, + REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); controllers.add(mAppButtonsPreferenceController); controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); @@ -318,6 +321,23 @@ public class AppInfoDashboardFragment extends DashboardFragment return true; } + /** + * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}. + * If it's not, the fragment will finish. + * + * @return true if package is displayable. + */ + @VisibleForTesting + boolean ensureDisplayableModule(Activity activity) { + if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) { + mFinishing = true; + Log.w(TAG, "Package is hidden module, exiting: " + mPackageName); + activity.finishAndRemoveTask(); + return false; + } + return true; + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java index 9e44c955ed9..56e59f7bd0e 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java @@ -17,8 +17,7 @@ package com.android.settings.applications.appinfo; import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.ARG_PACKAGE_NAME; -import static com.android.settings.applications.appinfo.AppInfoDashboardFragment - .UNINSTALL_ALL_USERS_MENU; +import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.UNINSTALL_ALL_USERS_MENU; import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.UNINSTALL_UPDATES; import static com.google.common.truth.Truth.assertThat; @@ -43,6 +42,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.UserManager; +import android.util.ArraySet; import android.view.Menu; import android.view.MenuItem; @@ -53,6 +53,7 @@ import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.lifecycle.Lifecycle; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,10 +64,14 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.List; +import java.util.Set; @RunWith(RobolectricTestRunner.class) public final class AppInfoDashboardFragmentTest { @@ -101,6 +106,11 @@ public final class AppInfoDashboardFragmentTest { (InstantAppDataProvider) (i -> false)); } + @After + public void tearDown() { + ShadowAppUtils.reset(); + } + @Test public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); @@ -186,6 +196,24 @@ public final class AppInfoDashboardFragmentTest { verify(mActivity, never()).finishAndRemoveTask(); } + @Test + @Config(shadows = ShadowAppUtils.class) + public void ensureDisplayableModule_hiddenModule_shouldReturnFalse() { + ShadowAppUtils.addHiddenModule(PACKAGE_NAME); + ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); + + + assertThat(mFragment.ensureDisplayableModule(mActivity)).isFalse(); + } + + @Test + @Config(shadows = ShadowAppUtils.class) + public void ensureDisplayableModule_regularApp_shouldReturnTrue() { + ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); + + assertThat(mFragment.ensureDisplayableModule(mActivity)).isTrue(); + } + @Test public void createPreference_hasNoPackageInfo_shouldSkip() { ReflectionHelpers.setField(mFragment, "mPackageInfo", null); @@ -240,7 +268,8 @@ public final class AppInfoDashboardFragmentTest { doReturn(true).when(mFragment).refreshUi(); mFragment - .onActivityResult(AppInfoDashboardFragment.REQUEST_UNINSTALL, 0, mock(Intent.class)); + .onActivityResult(AppInfoDashboardFragment.REQUEST_UNINSTALL, 0, + mock(Intent.class)); verify(mActivity).invalidateOptionsMenu(); } @@ -347,4 +376,24 @@ public final class AppInfoDashboardFragmentTest { .containsKey(ARG_PACKAGE_NAME)) .isTrue(); } + + @Implements(AppUtils.class) + public static class ShadowAppUtils { + + public static Set sHiddenModules = new ArraySet<>(); + + @Resetter + public static void reset() { + sHiddenModules.clear(); + } + + public static void addHiddenModule(String pkg) { + sHiddenModules.add(pkg); + } + + @Implementation + protected static boolean isHiddenSystemModule(Context context, String packageName) { + return sHiddenModules.contains(packageName); + } + } } From 47113af71979d768de2d671d2cc545d44a9f2461 Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Wed, 27 Mar 2019 15:17:07 -0700 Subject: [PATCH 26/27] Add a string for prompting which SIM to use for SMS In some multi-SIM cases the telephony stack will fire an intent that Settings listens for, specifying that Settings should present a dialog to the user asking them which SIM card they want to use for various services such as Data, Calls, and SMS. The string we were using for SMS was a general "Select SIM card". This CL adds a more specific one that should make more sense to the user. Bug: 129021763 Test: make Settings Change-Id: I8c422a7e6de3ffd1255bc7b4124d9a1be6adf633 --- res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index e51734c464b..aff6a90db30 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7016,6 +7016,8 @@ Always use this for calls Select a SIM for data + + Select a SIM for SMS Switching data SIM, this may take up to a minute... From 7771fa5a95ed1b91f9a5ffef80cb30c4edb0c064 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 27 Mar 2019 23:15:32 +0000 Subject: [PATCH 27/27] Revert "Remove advanced button from location settings" This reverts commit c7cef5ee43f0678998216bb2bc77c1cd9382cc2b. Reason for revert: b/124059127 Change-Id: I34e788f6959b94f27b4e25f1ca925777acb9a1d0 --- res/xml/location_settings.xml | 83 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index 7eccd23a5ee..29d4048c22f 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -14,49 +14,50 @@ limitations under the License. --> - + - - - - - - - - - - - - - - - + - + android:key="location_advanced_settings" + settings:initialExpandedChildrenCount="2"> - + + + + + + + + + + + + + + + +