From 15af7d7d5586fd4d9f30ce745679257e12f9c6bc Mon Sep 17 00:00:00 2001 From: t Date: Thu, 2 Nov 2023 08:06:59 +0000 Subject: [PATCH 01/13] Disable factory reset in DSU mode Bug: 302317901 Bug: 316578327 Test: build Merged-In: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe Change-Id: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe --- src/com/android/settings/MainClear.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java index f706c785401..07888c6b676 100644 --- a/src/com/android/settings/MainClear.java +++ b/src/com/android/settings/MainClear.java @@ -26,11 +26,13 @@ import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.ActionBar; import android.app.Activity; +import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -43,6 +45,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.image.DynamicSystemManager; import android.provider.Settings; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; @@ -266,6 +269,19 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis return; } + final DynamicSystemManager dsuManager = (DynamicSystemManager) + getActivity().getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); + if (dsuManager.isInUse()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dsu_is_running); + builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) {} + }); + AlertDialog dsuAlertdialog = builder.create(); + dsuAlertdialog.show(); + return; + } + if (runKeyguardConfirmation(KEYGUARD_REQUEST)) { return; } From 0764d71e8215184fa718a4c5968cbbe25fdaa0b3 Mon Sep 17 00:00:00 2001 From: t Date: Thu, 2 Nov 2023 08:06:59 +0000 Subject: [PATCH 02/13] Disable factory reset in DSU mode Bug: 302317901 Bug: 316578327 Test: build Merged-In: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe Change-Id: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe --- src/com/android/settings/MainClear.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java index 1434d7216a9..ac073b12e81 100644 --- a/src/com/android/settings/MainClear.java +++ b/src/com/android/settings/MainClear.java @@ -26,11 +26,13 @@ import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.ActionBar; import android.app.Activity; +import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -43,6 +45,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.image.DynamicSystemManager; import android.provider.Settings; import android.sysprop.VoldProperties; import android.telephony.euicc.EuiccManager; @@ -267,6 +270,19 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis return; } + final DynamicSystemManager dsuManager = (DynamicSystemManager) + getActivity().getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); + if (dsuManager.isInUse()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dsu_is_running); + builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) {} + }); + AlertDialog dsuAlertdialog = builder.create(); + dsuAlertdialog.show(); + return; + } + if (runKeyguardConfirmation(KEYGUARD_REQUEST)) { return; } From 76260a7884d54758d4d47ae7c6043894bc96d4e3 Mon Sep 17 00:00:00 2001 From: t Date: Thu, 2 Nov 2023 08:06:59 +0000 Subject: [PATCH 03/13] Disable factory reset in DSU mode Bug: 302317901 Bug: 316578327 Test: build Merged-In: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe Change-Id: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe --- src/com/android/settings/MainClear.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java index 8a441e2c2ce..4dce18a016a 100644 --- a/src/com/android/settings/MainClear.java +++ b/src/com/android/settings/MainClear.java @@ -26,11 +26,13 @@ import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.ActionBar; import android.app.Activity; +import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -43,6 +45,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.image.DynamicSystemManager; import android.provider.Settings; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; @@ -266,6 +269,19 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis return; } + final DynamicSystemManager dsuManager = (DynamicSystemManager) + getActivity().getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); + if (dsuManager.isInUse()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dsu_is_running); + builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) {} + }); + AlertDialog dsuAlertdialog = builder.create(); + dsuAlertdialog.show(); + return; + } + if (runKeyguardConfirmation(KEYGUARD_REQUEST)) { return; } From d45a07894b5b9193ad6338c7db0cd635f1aa89ef Mon Sep 17 00:00:00 2001 From: t Date: Thu, 2 Nov 2023 08:06:59 +0000 Subject: [PATCH 04/13] Disable factory reset in DSU mode Bug: 302317901 Bug: 316578327 Test: build Merged-In: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe Change-Id: I485eb6ac7beec0893d91ca5fe8ad88ecd96a5cbe --- src/com/android/settings/MainClear.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java index 2b0f01036ea..9fe11a48502 100644 --- a/src/com/android/settings/MainClear.java +++ b/src/com/android/settings/MainClear.java @@ -26,11 +26,13 @@ import android.accounts.AccountManager; import android.accounts.AuthenticatorDescription; import android.app.ActionBar; import android.app.Activity; +import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -43,6 +45,7 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.image.DynamicSystemManager; import android.provider.Settings; import android.sysprop.VoldProperties; import android.telephony.euicc.EuiccManager; @@ -266,6 +269,19 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis return; } + final DynamicSystemManager dsuManager = (DynamicSystemManager) + getActivity().getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); + if (dsuManager.isInUse()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dsu_is_running); + builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) {} + }); + AlertDialog dsuAlertdialog = builder.create(); + dsuAlertdialog.show(); + return; + } + if (runKeyguardConfirmation(KEYGUARD_REQUEST)) { return; } From dfeeb297adba4b63df278c364278c74dd15167f9 Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Tue, 22 Oct 2024 14:39:48 +0800 Subject: [PATCH 05/13] Update backgroundcolor of advanced bt header image BUG: 362547405 Test: local tested Flag: EXEMPT minor UI fix Change-Id: I3aa400a64b95f067b380ed94564ee180341e8b70 --- res/drawable/bt_header_circle_outline.xml | 23 +++++++++++++++++++++++ res/layout/advanced_bt_entity_sub.xml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 res/drawable/bt_header_circle_outline.xml diff --git a/res/drawable/bt_header_circle_outline.xml b/res/drawable/bt_header_circle_outline.xml new file mode 100644 index 00000000000..ef7a5326ba0 --- /dev/null +++ b/res/drawable/bt_header_circle_outline.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml index dd8e43a0f73..252ab47f98b 100644 --- a/res/layout/advanced_bt_entity_sub.xml +++ b/res/layout/advanced_bt_entity_sub.xml @@ -34,7 +34,7 @@ android:layout_height="72dp" android:layout_gravity="center" android:antialias="true" - android:background="@drawable/circle_outline" + android:background="@drawable/bt_header_circle_outline" android:padding="8dp" android:scaleType="fitCenter" /> From b415befd5324091938fc441999d13fecd9b01cd3 Mon Sep 17 00:00:00 2001 From: David Liu Date: Wed, 23 Oct 2024 02:00:44 +0000 Subject: [PATCH 06/13] Migrate Adaptive Connectivity Add the skeleton of the Adaptive Connectivity Bug: 368359961 Test: atest AdaptiveConnectivityScreenTest Flag: com.android.settings.flags.catalyst_adaptive_connectivity Change-Id: I45aa92635780230c8b89205cd13b7d968438e424 --- aconfig/catalyst/network_and_internet.aconfig | 7 +++ .../network/AdaptiveConnectivityScreen.kt | 45 +++++++++++++++++++ .../network/AdaptiveConnectivitySettings.java | 11 ++++- .../network/AdaptiveConnectivityScreenTest.kt | 38 ++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/network/AdaptiveConnectivityScreen.kt create mode 100644 tests/robotests/src/com/android/settings/network/AdaptiveConnectivityScreenTest.kt diff --git a/aconfig/catalyst/network_and_internet.aconfig b/aconfig/catalyst/network_and_internet.aconfig index a5183f3738a..e6a190aeb7b 100644 --- a/aconfig/catalyst/network_and_internet.aconfig +++ b/aconfig/catalyst/network_and_internet.aconfig @@ -14,3 +14,10 @@ flag { description: "Flag for SIMs" bug: "323791114" } + +flag { + name: "catalyst_adaptive_connectivity" + namespace: "android_settings" + description: "Flag for Adaptive connectivity" + bug: "323791114" +} diff --git a/src/com/android/settings/network/AdaptiveConnectivityScreen.kt b/src/com/android/settings/network/AdaptiveConnectivityScreen.kt new file mode 100644 index 00000000000..99f402f3f65 --- /dev/null +++ b/src/com/android/settings/network/AdaptiveConnectivityScreen.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network + +import android.content.Context +import com.android.settings.R +import com.android.settings.flags.Flags +import com.android.settingslib.metadata.ProvidePreferenceScreen +import com.android.settingslib.metadata.preferenceHierarchy +import com.android.settingslib.preference.PreferenceScreenCreator + +@ProvidePreferenceScreen +class AdaptiveConnectivityScreen : PreferenceScreenCreator { + override val key + get() = KEY + + override val title + get() = R.string.adaptive_connectivity_title + + override fun isFlagEnabled(context: Context) = Flags.catalystAdaptiveConnectivity() + + override fun fragmentClass() = AdaptiveConnectivitySettings::class.java + + override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + + override fun hasCompleteHierarchy() = false + + companion object { + const val KEY = "adaptive_connectivity" + } +} diff --git a/src/com/android/settings/network/AdaptiveConnectivitySettings.java b/src/com/android/settings/network/AdaptiveConnectivitySettings.java index 5e1dc124f3b..a4e872c67cb 100644 --- a/src/com/android/settings/network/AdaptiveConnectivitySettings.java +++ b/src/com/android/settings/network/AdaptiveConnectivitySettings.java @@ -16,6 +16,10 @@ package com.android.settings.network; import android.app.settings.SettingsEnums; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -30,8 +34,6 @@ public class AdaptiveConnectivitySettings extends DashboardFragment { private static final String TAG = "AdaptiveConnectivitySettings"; - private static final String KEY_ADAPTIVE_CONNECTIVITY_PREFERENCE = "adaptive_connectivity"; - @Override public int getMetricsCategory() { return SettingsEnums.ADAPTIVE_CONNECTIVITY_CATEGORY; @@ -49,4 +51,9 @@ public class AdaptiveConnectivitySettings extends DashboardFragment { public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.adaptive_connectivity_settings); + + @Override + public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) { + return AdaptiveConnectivityScreen.KEY; + } } diff --git a/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityScreenTest.kt b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityScreenTest.kt new file mode 100644 index 00000000000..f575fe78256 --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityScreenTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.flags.Flags +import com.android.settingslib.preference.CatalystScreenTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class AdaptiveConnectivityScreenTest : CatalystScreenTestCase() { + override val preferenceScreenCreator = AdaptiveConnectivityScreen() + override val flagName + get() = Flags.FLAG_CATALYST_ADAPTIVE_CONNECTIVITY + + override fun migration() {} + + @Test + fun key() { + assertThat(preferenceScreenCreator.key).isEqualTo(AdaptiveConnectivityScreen.KEY) + } +} From 1694adb1aa39cd6ecc02df6cadf5e6c0ff82bf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 23 Oct 2024 18:02:54 +0200 Subject: [PATCH 07/13] Change the icon in the trigger segment for TYPE_DRIVING modes Use the "settings gear" icon instead of the "car" icon. Fixes: 369326738 Test: manual Flag: android.app.modes_ui Change-Id: I49d7089558fcd0b9e02020a8ad215bc23ebb8e0e --- .../ic_zen_mode_trigger_with_settings.xml | 26 +++++++++++++++++++ ...ModeTriggerUpdatePreferenceController.java | 4 +-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 res/drawable/ic_zen_mode_trigger_with_settings.xml diff --git a/res/drawable/ic_zen_mode_trigger_with_settings.xml b/res/drawable/ic_zen_mode_trigger_with_settings.xml new file mode 100644 index 00000000000..7dd81f11b4a --- /dev/null +++ b/res/drawable/ic_zen_mode_trigger_with_settings.xml @@ -0,0 +1,26 @@ + + + + \ No newline at end of file diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java index 193363585f1..014a1905b3b 100644 --- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java @@ -174,8 +174,8 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference @DrawableRes int icon; if (mode.getType() == TYPE_BEDTIME) { icon = com.android.internal.R.drawable.ic_zen_mode_type_schedule_time; // Clock - } else if (mode.getType() == TYPE_DRIVING) { - icon = com.android.internal.R.drawable.ic_zen_mode_type_driving; // Car + } else if (mode.getType() == TYPE_DRIVING && configurationIntent != null) { + icon = R.drawable.ic_zen_mode_trigger_with_settings; // Gear } else { icon = configurationIntent != null ? R.drawable.ic_zen_mode_trigger_with_activity : R.drawable.ic_zen_mode_trigger_without_activity; From 45a2c3e5b86c60bf24c2f6541f2eddab1a7b6e17 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 23 Oct 2024 18:09:45 +0800 Subject: [PATCH 08/13] Refactor DarkModePreference for catalyst Bug: 375132235 Flag: EXEMPT refactor Test: N/A Change-Id: I17a63ad62d3b9fced4305d464372f2eab5023e3f --- .../display/darkmode/DarkModePreference.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/display/darkmode/DarkModePreference.java b/src/com/android/settings/display/darkmode/DarkModePreference.java index a1440eec9c9..ff211317b12 100644 --- a/src/com/android/settings/display/darkmode/DarkModePreference.java +++ b/src/com/android/settings/display/darkmode/DarkModePreference.java @@ -14,7 +14,6 @@ package com.android.settings.display.darkmode; -import android.app.UiModeManager; import android.content.Context; import android.content.res.Configuration; import android.os.PowerManager; @@ -28,39 +27,44 @@ import com.android.settingslib.PrimarySwitchPreference; */ public class DarkModePreference extends PrimarySwitchPreference { - private UiModeManager mUiModeManager; private DarkModeObserver mDarkModeObserver; - private PowerManager mPowerManager; - private Runnable mCallback; - - private TimeFormatter mFormat; + private boolean isCatalystEnabled; public DarkModePreference(Context context, AttributeSet attrs) { super(context, attrs); - mDarkModeObserver = new DarkModeObserver(context); - mUiModeManager = context.getSystemService(UiModeManager.class); - mPowerManager = context.getSystemService(PowerManager.class); - mFormat = new TimeFormatter(context); - mCallback = () -> { - final boolean batterySaver = mPowerManager.isPowerSaveMode(); - final boolean active = (getContext().getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_YES) != 0; - setSwitchEnabled(!batterySaver); - updateSummary(batterySaver, active); - }; - mDarkModeObserver.subscribe(mCallback); + } + + /** + * Sets if catalyst is enabled on the preference. + */ + public void setCatalystEnabled(boolean catalystEnabled) { + isCatalystEnabled = catalystEnabled; } @Override public void onAttached() { super.onAttached(); - mDarkModeObserver.subscribe(mCallback); + if (!isCatalystEnabled) { + Context context = getContext(); + mDarkModeObserver = new DarkModeObserver(context); + Runnable callback = () -> { + PowerManager powerManager = context.getSystemService(PowerManager.class); + final boolean batterySaver = powerManager.isPowerSaveMode(); + final boolean active = (context.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_YES) != 0; + setSwitchEnabled(!batterySaver); + updateSummary(batterySaver, active); + }; + mDarkModeObserver.subscribe(callback); + } } @Override public void onDetached() { super.onDetached(); - mDarkModeObserver.unsubscribe(); + if (!isCatalystEnabled) { + mDarkModeObserver.unsubscribe(); + } } private void updateSummary(boolean batterySaver, boolean active) { From 62c2800b3fd0b8f5ef3e5b6120e6567520787b35 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 24 Oct 2024 02:34:54 +0000 Subject: [PATCH 09/13] Fix test failure Bug: 375011040 Test: atest Flag: EXEMPT fix test Change-Id: Id2ecf785655dfcca9f65fc3ee64ffae11fc78e88 --- .../applications/AppWithAdminGrantedPermissionsCounterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java index 2a09cdeb0d4..2da25f18891 100644 --- a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java @@ -57,7 +57,7 @@ import java.util.Collections; public final class AppWithAdminGrantedPermissionsCounterTest { @Rule - MockitoRule mMockitoRule = MockitoJUnit.rule(); + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private final String APP_1 = "app1"; private final String APP_2 = "app2"; From 60521ee2f9ac47eb5a4e92acdafe824051c38e49 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 10 Oct 2024 15:14:29 +0800 Subject: [PATCH 10/13] Add WifiCallingScreen and corresponding TS flag Bug: 372732219 Test: atest Flag: com.android.settings.flags.catalyst_wifi_calling_settings Change-Id: I3d78b6745e1127a0a7e73ec20ae95e9c9db2c139 --- aconfig/catalyst/network_and_internet.aconfig | 7 +++ res/xml/wifi_calling_settings.xml | 2 +- .../wifi/calling/WifiCallingScreen.kt | 45 +++++++++++++++++++ .../calling/WifiCallingSettingsForSub.java | 5 +++ .../wifi/calling/WifiCallingScreenTest.kt | 36 +++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/wifi/calling/WifiCallingScreen.kt create mode 100644 tests/robotests/src/com/android/settings/wifi/calling/WifiCallingScreenTest.kt diff --git a/aconfig/catalyst/network_and_internet.aconfig b/aconfig/catalyst/network_and_internet.aconfig index 99aec82c5e9..130905f538a 100644 --- a/aconfig/catalyst/network_and_internet.aconfig +++ b/aconfig/catalyst/network_and_internet.aconfig @@ -28,3 +28,10 @@ flag { description: "Flag for Adaptive connectivity" bug: "323791114" } + +flag { + name: "catalyst_wifi_calling" + namespace: "android_settings" + description: "Flag for Wi-Fi calling screen" + bug: "323791114" +} diff --git a/res/xml/wifi_calling_settings.xml b/res/xml/wifi_calling_settings.xml index c45f702ca13..2ce408b98dc 100644 --- a/res/xml/wifi_calling_settings.xml +++ b/res/xml/wifi_calling_settings.xml @@ -16,7 +16,7 @@ Date: Wed, 23 Oct 2024 18:16:16 +0800 Subject: [PATCH 11/13] [Catalyst] Migrate "Dark theme" settings Bug: 375132235 Flag: com.android.settings.flags.catalyst_dark_ui_mode Test: Manual Change-Id: I40f305fe8d89feac84e2316c7a90c6eeb2b4a5fa --- aconfig/catalyst/display.aconfig | 8 + .../display/DarkUIPreferenceController.java | 2 + .../android/settings/display/DisplayScreen.kt | 5 +- .../display/darkmode/DarkModeScreen.kt | 171 ++++++++++++++++++ 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/display/darkmode/DarkModeScreen.kt diff --git a/aconfig/catalyst/display.aconfig b/aconfig/catalyst/display.aconfig index 94a01e02f7b..038a9b0c632 100644 --- a/aconfig/catalyst/display.aconfig +++ b/aconfig/catalyst/display.aconfig @@ -14,3 +14,11 @@ flag { description: "Flag for Screen Timeout settings" bug: "323791114" } + +flag { + name: "catalyst_dark_ui_mode" + namespace: "android_settings" + description: "Flag for Dark theme" + bug: "323791114" +} + diff --git a/src/com/android/settings/display/DarkUIPreferenceController.java b/src/com/android/settings/display/DarkUIPreferenceController.java index f1bbfcd47b7..26b33da3729 100644 --- a/src/com/android/settings/display/DarkUIPreferenceController.java +++ b/src/com/android/settings/display/DarkUIPreferenceController.java @@ -34,6 +34,7 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +// LINT.IfChange public class DarkUIPreferenceController extends TogglePreferenceController implements LifecycleObserver, OnStart, OnStop { @@ -123,3 +124,4 @@ public class DarkUIPreferenceController extends TogglePreferenceController imple return AVAILABLE; } } +// LINT.ThenChange(darkmode/DarkModeScreen.kt) diff --git a/src/com/android/settings/display/DisplayScreen.kt b/src/com/android/settings/display/DisplayScreen.kt index 7b1d000d0dc..6c7630b6fd6 100644 --- a/src/com/android/settings/display/DisplayScreen.kt +++ b/src/com/android/settings/display/DisplayScreen.kt @@ -18,6 +18,7 @@ package com.android.settings.display import android.content.Context import com.android.settings.DisplaySettings import com.android.settings.R +import com.android.settings.display.darkmode.DarkModeScreen import com.android.settings.flags.Flags import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceIconProvider @@ -46,7 +47,9 @@ class DisplayScreen : override fun fragmentClass() = DisplaySettings::class.java - override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { + +DarkModeScreen.KEY + } override fun isAvailable(context: Context) = context.resources.getBoolean(R.bool.config_show_top_level_display) diff --git a/src/com/android/settings/display/darkmode/DarkModeScreen.kt b/src/com/android/settings/display/darkmode/DarkModeScreen.kt new file mode 100644 index 00000000000..263958cd335 --- /dev/null +++ b/src/com/android/settings/display/darkmode/DarkModeScreen.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 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.display.darkmode + +import android.app.UiModeManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.Configuration +import android.os.PowerManager +import androidx.preference.Preference +import com.android.settings.R +import com.android.settings.flags.Flags +import com.android.settingslib.PrimarySwitchPreference +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.NoOpKeyedObservable +import com.android.settingslib.metadata.BooleanValue +import com.android.settingslib.metadata.PersistentPreference +import com.android.settingslib.metadata.PreferenceLifecycleContext +import com.android.settingslib.metadata.PreferenceLifecycleProvider +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.metadata.ProvidePreferenceScreen +import com.android.settingslib.metadata.preferenceHierarchy +import com.android.settingslib.preference.PreferenceScreenBinding +import com.android.settingslib.preference.PreferenceScreenCreator +import java.util.WeakHashMap + +// LINT.IfChange +@ProvidePreferenceScreen +class DarkModeScreen : + PreferenceScreenCreator, + PreferenceScreenBinding, + PersistentPreference, + BooleanValue, + PreferenceSummaryProvider, + PreferenceLifecycleProvider { + + /** + * States for different screens. + * + * The "Dark mode" appears in several screens. And in Android split-screen mode, more than one + * "Dark mode" settings could be displayed at the same time. As [PreferenceScreenCreator] works + * like singleton, we need to register different broadcast receivers for different screens. + */ + private val fragmentStates = WeakHashMap() + + override val key: String + get() = KEY + + override val title: Int + get() = R.string.dark_ui_mode + + override val keywords: Int + get() = R.string.keywords_dark_ui_mode + + override fun isFlagEnabled(context: Context) = Flags.catalystDarkUiMode() + + override fun fragmentClass() = DarkModeSettingsFragment::class.java + + override fun hasCompleteHierarchy() = false + + override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + + override fun storage(context: Context): KeyValueStore = DarkModeStorage(context) + + override fun createWidget(context: Context) = PrimarySwitchPreference(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + if (preference is DarkModePreference) preference.setCatalystEnabled(true) + val context = preference.context + val primarySwitchPreference = preference as PrimarySwitchPreference + primarySwitchPreference.isSwitchEnabled = !context.isPowerSaveMode() + primarySwitchPreference.isChecked = context.isDarkMode() + } + + override fun isEnabled(context: Context) = !context.isPowerSaveMode() + + override fun getSummary(context: Context): CharSequence? { + val active = context.isDarkMode() + return when { + !context.isPowerSaveMode() -> AutoDarkTheme.getStatus(context, active) + active -> context.getString(R.string.dark_ui_mode_disabled_summary_dark_theme_on) + else -> context.getString(R.string.dark_ui_mode_disabled_summary_dark_theme_off) + } + } + + override fun onStart(context: PreferenceLifecycleContext) { + val broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(receiverContext: Context, intent: Intent) { + context.notifyPreferenceChange(this@DarkModeScreen) + } + } + context.registerReceiver( + broadcastReceiver, + IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED), + ) + + val darkModeObserver = DarkModeObserver(context) + darkModeObserver.subscribe { context.notifyPreferenceChange(this@DarkModeScreen) } + + fragmentStates[context] = FragmentState(broadcastReceiver, darkModeObserver) + } + + override fun onStop(context: PreferenceLifecycleContext) { + fragmentStates.remove(context)?.run { + context.unregisterReceiver(broadcastReceiver) + darkModeObserver.unsubscribe() + } + } + + private class FragmentState( + val broadcastReceiver: BroadcastReceiver, + val darkModeObserver: DarkModeObserver, + ) + + /** + * Abstract storage for dark mode settings. + * + * The underlying storage is manipulated by [UiModeManager] but we do not need to worry about + * the details. Additionally, the observer is for UI purpose only right now, so use + * [NoOpKeyedObservable]. + */ + @Suppress("UNCHECKED_CAST") + private class DarkModeStorage(private val context: Context) : + NoOpKeyedObservable(), KeyValueStore { + + override fun contains(key: String) = key == KEY + + override fun getValue(key: String, valueType: Class) = + when { + key == KEY && valueType == Boolean::class.javaObjectType -> + context.isDarkMode() as T + else -> null + } + + override fun setValue(key: String, valueType: Class, value: T?) { + if (key == KEY && value is Boolean) { + context.getSystemService(UiModeManager::class.java)?.setNightModeActivated(value) + } + } + } + + companion object { + const val KEY = "dark_ui_mode" + + private fun Context.isPowerSaveMode() = + getSystemService(PowerManager::class.java)?.isPowerSaveMode == true + + private fun Context.isDarkMode() = + (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES) != 0 + } +} +// LINT.ThenChange(../DarkUIPreferenceController.java) From 6825f23d07815b8b3a3537c3f66572dd249ebc0d Mon Sep 17 00:00:00 2001 From: tomhsu Date: Thu, 24 Oct 2024 08:41:35 +0000 Subject: [PATCH 12/13] Fix dialog message overlap title Flag: EXEMPT bug fix Fix: b/326853900 Test: Manual test. see b/326853900#4 Change-Id: Id6e4e27eba5a90820d93802ff4a7c3fb1f053190 --- .../settings/network/EraseEuiccDataDialogFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java index b8e18b31dee..ad02ddb6f45 100644 --- a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java +++ b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java @@ -16,7 +16,6 @@ package com.android.settings.network; -import android.app.AlertDialog; import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; @@ -27,13 +26,14 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -import com.android.settings.system.ResetDashboardFragment; import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settings.system.ResetDashboardFragment; public class EraseEuiccDataDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { From a9e8225ba471bc5260b7caa60b3edbdca8e82d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Thu, 24 Oct 2024 16:16:00 +0200 Subject: [PATCH 13/13] Update summary of Modes entry in Settings Instead of the number of automatic modes, simply list the first (up to 3) existing modes, according to the standard sort order, when none are active. As a consequence, the summary is never empty now. Fixes: 374179941 Test: atest ZenModeSummaryHelperTest + manual Flag: android.app.modes_ui Change-Id: I04f9d90a5e1e4ed13d66181b85d77fef8c6ff256 --- res/values/strings.xml | 10 ++- .../modes/ZenModeSummaryHelper.java | 36 ++++---- .../modes/ZenModeSummaryHelperTest.java | 82 +++++++++++-------- 3 files changed, 70 insertions(+), 58 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index ebb69f7e4a8..2ee6d60d877 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8149,11 +8149,13 @@ - + + {count, plural, - =0 {} - =1 {1 mode can turn on automatically} - other {# modes can turn on automatically} + =0 {Do Not Disturb} + =1 {{mode_1}} + =2 {{mode_1}, {mode_2}} + other {{mode_1}, {mode_2}, {mode_3}} } diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java index 483b8f07d20..2a0e8b354b5 100644 --- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java +++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java @@ -498,29 +498,27 @@ class ZenModeSummaryHelper { MessageFormat msgFormat = new MessageFormat( mContext.getString(R.string.zen_modes_summary_some_active), Locale.getDefault()); - - Map args = new HashMap<>(); - args.put("count", activeModes.size()); - args.put("mode_1", activeModes.get(0).getName()); - if (activeModes.size() >= 2) { - args.put("mode_2", activeModes.get(1).getName()); - if (activeModes.size() == 3) { - args.put("mode_3", activeModes.get(2).getName()); - } - } - - return msgFormat.format(args); + return buildModesSummary(msgFormat, activeModes); } else { - int automaticModeCount = (int) modes.stream() - .filter(m -> m.isEnabled() && !m.isManualDnd() && !m.isCustomManual()) - .count(); - MessageFormat msgFormat = new MessageFormat( - mContext.getString(R.string.zen_modes_summary_none_active), + mContext.getString(R.string.zen_modes_summary), Locale.getDefault()); - Map msgArgs = Map.of("count", automaticModeCount); - return msgFormat.format(msgArgs); + return buildModesSummary(msgFormat, modes); } } + private static String buildModesSummary(MessageFormat msgFormat, List modes) { + Map args = new HashMap<>(); + args.put("count", modes.size()); + if (modes.size() >= 1) { + args.put("mode_1", modes.get(0).getName()); + if (modes.size() >= 2) { + args.put("mode_2", modes.get(1).getName()); + if (modes.size() >= 3) { + args.put("mode_3", modes.get(2).getName()); + } + } + } + return msgFormat.format(args); + } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java index a0e1d38f24d..1cd3053cb5d 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java @@ -29,7 +29,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.robolectric.Shadows.shadowOf; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -38,9 +37,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.SystemZenRules; import android.service.notification.ZenDeviceEffects; -import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -476,46 +473,61 @@ public class ZenModeSummaryHelperTest { } @Test - public void getModesSummary_noRules_noSummary() { + public void getModesSummary_noModesWtf_fallbackSummary() { String summary = mSummaryHelper.getModesSummary(ImmutableList.of()); - assertThat(summary).isEmpty(); + assertThat(summary).isEqualTo("Do Not Disturb"); } @Test - public void getModesSummary_onlyDndAndNotActive_noSummary() { - ImmutableList modes = ImmutableList.of(TestModeBuilder.MANUAL_DND_INACTIVE); - String summary = mSummaryHelper.getModesSummary(modes); - assertThat(summary).isEmpty(); - } - - @Test - public void getModesSummary_noRulesActive_countsOnlyEnabledAutomaticModes() { + public void getModesSummary_oneMode_listsMode() { ImmutableList modes = ImmutableList.of( - TestModeBuilder.MANUAL_DND_INACTIVE, // Not automatic - new TestModeBuilder().setName("Auto 1").build(), // App provided automatic - new TestModeBuilder() - .setName("Custom manual 1") - .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setType(AutomaticZenRule.TYPE_OTHER) - .setConditionId(ZenModeConfig.toCustomManualConditionId()) - .build(), // Custom manual, not automatic - new TestModeBuilder() - .setName("Disabled 1") - .setEnabled(false) - .build(), // Would be automatic, but it's disabled. - new TestModeBuilder() - .setName("Sleep") - .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setType(AutomaticZenRule.TYPE_SCHEDULE_TIME) - .build() // Time based, automatic. + new TestModeBuilder().setName("Surfing").build() ); String summary = mSummaryHelper.getModesSummary(modes); - assertThat(summary).isEqualTo("2 modes can turn on automatically"); + assertThat(summary).isEqualTo("Surfing"); } @Test - public void getModesSummary_oneModeActive_listsMode() { + public void getModesSummary_twoModes_listsModes() { + ImmutableList modes = ImmutableList.of( + new TestModeBuilder().setName("Cartwheeling").build(), + new TestModeBuilder().setName("Hula-hooping").build() + ); + + String summary = mSummaryHelper.getModesSummary(modes); + assertThat(summary).isEqualTo("Cartwheeling, Hula-hooping"); + } + + @Test + public void getModesSummary_threeModes_listsModes() { + ImmutableList modes = ImmutableList.of( + new TestModeBuilder().setName("Prancing").build(), + new TestModeBuilder().setName("Hopping").build(), + new TestModeBuilder().setName("Skipping").build() + ); + + String summary = mSummaryHelper.getModesSummary(modes); + assertThat(summary).isEqualTo("Prancing, Hopping, Skipping"); + } + + @Test + public void getModesSummary_manyModes_listsThreeModes() { + ImmutableList modes = ImmutableList.of( + new TestModeBuilder().setName("Juggling").build(), + new TestModeBuilder().setName("Rhyming").build(), + new TestModeBuilder().setName("Meandering").build(), + new TestModeBuilder().setName("Doodling").build(), + new TestModeBuilder().setName("Whistling").build(), + new TestModeBuilder().setName("Lounging").build() + ); + + String summary = mSummaryHelper.getModesSummary(modes); + assertThat(summary).isEqualTo("Juggling, Rhyming, Meandering"); + } + + @Test + public void getModesSummary_oneModeActive_listsActiveMode() { ImmutableList modes = ImmutableList.of( TestModeBuilder.MANUAL_DND_ACTIVE, new TestModeBuilder().setName("Inactive").setActive(false).build()); @@ -525,7 +537,7 @@ public class ZenModeSummaryHelperTest { } @Test - public void getModesSummary_twoModesActive_listsModes() { + public void getModesSummary_twoModesActive_listsActiveModes() { ImmutableList modes = ImmutableList.of( TestModeBuilder.MANUAL_DND_ACTIVE, new TestModeBuilder().setName("Inactive").setActive(false).build(), @@ -536,7 +548,7 @@ public class ZenModeSummaryHelperTest { } @Test - public void getModesSummary_threeModesActive_listsModes() { + public void getModesSummary_threeModesActive_listsActiveModes() { ImmutableList modes = ImmutableList.of( TestModeBuilder.MANUAL_DND_INACTIVE, new TestModeBuilder().setName("Inactive #1").setActive(false).build(), @@ -550,7 +562,7 @@ public class ZenModeSummaryHelperTest { } @Test - public void getModesSummary_manyModesActive_listsACouple() { + public void getModesSummary_manyModesActive_listsSomeActiveModes() { ImmutableList modes = ImmutableList.of( TestModeBuilder.MANUAL_DND_ACTIVE, new TestModeBuilder().setName("Inactive #1").setActive(false).build(),