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/AndroidManifest.xml b/AndroidManifest.xml index 4166d6f0624..9f6cdb50487 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -196,6 +196,19 @@ android:value="true" /> + + + + + + + + + + android:theme="@style/GlifTheme.Light"> @@ -3042,9 +3055,8 @@ 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/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_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. --> - - + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + android:autoMirrored="true" + android:tint="?android:attr/colorControlNormal"> + + + + 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/res/drawable/ic_settings_phone_idle.xml b/res/drawable/ic_settings_phone_idle.xml new file mode 100644 index 00000000000..7bb6c3192be --- /dev/null +++ b/res/drawable/ic_settings_phone_idle.xml @@ -0,0 +1,25 @@ + + + + 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/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"> 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/strings.xml b/res/values/strings.xml index e51734c464b..d8aec22677d 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... @@ -10924,7 +10926,7 @@ Privacy - Permissions, web activity, personal data + Permissions, account activity, personal data Remove @@ -10955,17 +10957,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/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/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/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"> - + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 75956572d33..bdb58895142 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -162,6 +162,7 @@ public class Settings extends SettingsActivity { public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ } public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ } public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ } + public static class MobileNetworkListActivity extends SettingsActivity {} // Top level categories for new IA public static class NetworkDashboardActivity extends SettingsActivity {} 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/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index b9b95387636..4ba605f9e96 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -18,6 +18,8 @@ package com.android.settings.accessibility; import static android.os.Vibrator.VibrationIntensity; +import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM; + import android.accessibilityservice.AccessibilityServiceInfo; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; @@ -594,6 +596,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements preference.setKey(componentName.flattenToString()); preference.setTitle(title); + preference.setIconSize(ICON_SIZE_MEDIUM); Utils.setSafeIcon(preference, icon); final boolean serviceEnabled = enabledServices.contains(componentName); String description = info.loadDescription(getPackageManager()); 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/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/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; } 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/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/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/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 10eec4e9c36..31d54f903fa 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -97,6 +97,7 @@ import com.android.settings.location.LocationSettings; import com.android.settings.location.ScanningSettings; import com.android.settings.network.ApnEditor; import com.android.settings.network.ApnSettings; +import com.android.settings.network.MobileNetworkListFragment; import com.android.settings.network.NetworkDashboardFragment; import com.android.settings.nfc.AndroidBeam; import com.android.settings.nfc.PaymentSettings; @@ -273,7 +274,8 @@ public class SettingsGateway { DataUsageList.class.getName(), ToggleBackupSettingFragment.class.getName(), PreviouslyConnectedDeviceDashboardFragment.class.getName(), - BatterySaverScheduleSettings.class.getName() + BatterySaverScheduleSettings.class.getName(), + MobileNetworkListFragment.class.getName() }; public static final String[] SETTINGS_FOR_RESTRICTED = { 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/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; + } +} 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; } 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); 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 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; } } diff --git a/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegate.java b/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegate.java new file mode 100644 index 00000000000..a4186b049c0 --- /dev/null +++ b/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegate.java @@ -0,0 +1,66 @@ +/* + * 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.homepage.contextualcards.slices; + +import android.content.Context; +import android.graphics.Canvas; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; + +public class SwipeDismissalDelegate extends ItemTouchHelper.Callback { + + private static final String TAG = "DismissItemTouchHelper"; + + public interface DismissalItemTouchHelperListener { + void onSwiped(int position); + } + + private final Context mContext; + private final DismissalItemTouchHelperListener mListener; + + public SwipeDismissalDelegate(Context context, DismissalItemTouchHelperListener listener) { + mContext = context; + mListener = listener; + } + + @Override + public int getMovementFlags(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder) { + return 0; + } + + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, + @NonNull RecyclerView.ViewHolder target) { + return false; + } + + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + mListener.onSwiped(viewHolder.getAdapterPosition()); + } + + @Override + public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, + @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, + boolean isCurrentlyActive) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } +} \ No newline at end of file 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); + } } } } 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/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index d89ad30f98e..e0974fe3466 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -479,7 +479,7 @@ public class ChooseLockPattern extends SettingsActivity { R.layout.choose_lock_pattern, container, false); layout.setHeaderText(getActivity().getTitle()); if (getResources().getBoolean(R.bool.config_lock_pattern_minimal_ui)) { - View iconView = layout.findViewById(R.id.suc_layout_icon); + View iconView = layout.findViewById(R.id.sud_layout_icon); if (iconView != null) { iconView.setVisibility(View.GONE); } 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. 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) 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; + } } 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(); 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(); 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/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/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); + } +} 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); 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); + } + } } 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( 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" 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); + } +} 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); diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java index 4bc83740575..48ebbecd884 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java @@ -32,7 +32,6 @@ import com.android.settingslib.testutils.DrawableTestHelper; import com.google.android.setupdesign.GlifLayout; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -93,7 +92,6 @@ public class ChooseLockPatternTest { @Config(qualifiers = "sw400dp") @Test - @Ignore("b/129342100") public void fingerprintExtraSet_shouldDisplayFingerprintIcon() { ChooseLockPattern activity = createActivity(true); ChooseLockPatternFragment fragment = (ChooseLockPatternFragment) @@ -109,7 +107,7 @@ public class ChooseLockPatternTest { ChooseLockPatternFragment fragment = (ChooseLockPatternFragment) activity.getSupportFragmentManager().findFragmentById(R.id.main_content); - View iconView = fragment.getView().findViewById(R.id.suc_layout_icon); + View iconView = fragment.getView().findViewById(R.id.sud_layout_icon); assertThat(iconView.getVisibility()).isEqualTo(View.GONE); } 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; } }