diff --git a/res/values/arrays.xml b/res/values/arrays.xml index e653c63a507..80c3e8d2c4b 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1059,6 +1059,20 @@ either_charging_or_docked + + @string/when_to_show_hubmode_never + @string/when_to_show_hubmode_charging + @string/when_to_show_hubmode_charging_and_upright + @string/when_to_show_hubmode_docked + + + + never + while_charging + while_charging_and_upright + while_docked + + @string/zen_mode_from_anyone @string/zen_mode_from_contacts diff --git a/res/values/strings.xml b/res/values/strings.xml index 003cd9bdc2a..129acbfa141 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3650,6 +3650,21 @@ Communal Communal settings + + Hub mode + + Widgets on lock screen + + + When to automatically show + + Never + + While charging + + While charging and upright + + While docked diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml index 9dc0bce18de..f839a477e8e 100644 --- a/res/xml/display_settings.xml +++ b/res/xml/display_settings.xml @@ -88,6 +88,12 @@ android:persistent="false" android:title="@string/accessibility_text_reading_options_title" settings:controller="com.android.settings.accessibility.TextReadingFragmentForDisplaySettingsController"/> + + + + diff --git a/res/xml/widgets_on_lockscreen_settings.xml b/res/xml/widgets_on_lockscreen_settings.xml new file mode 100644 index 00000000000..056df63ebd4 --- /dev/null +++ b/res/xml/widgets_on_lockscreen_settings.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/src/com/android/settings/communal/CommunalPreferenceController.java b/src/com/android/settings/communal/CommunalPreferenceController.java index e44afab90a6..16eb0e9dfc4 100644 --- a/src/com/android/settings/communal/CommunalPreferenceController.java +++ b/src/com/android/settings/communal/CommunalPreferenceController.java @@ -39,15 +39,14 @@ public class CommunalPreferenceController extends BasePreferenceController { * Returns whether communal preferences are available. */ public static boolean isAvailable(Context context) { + if (com.android.systemui.Flags.glanceableHubV2()) { + return false; + } + if (!Utils.canCurrentUserDream(context)) { return false; } - if (context.getResources().getBoolean(R.bool.config_show_communal_settings)) { - return true; - } - - return com.android.systemui.Flags.glanceableHubV2() - && context.getResources().getBoolean(R.bool.config_show_communal_settings_mobile); + return context.getResources().getBoolean(R.bool.config_show_communal_settings); } } diff --git a/src/com/android/settings/communal/WhenToStartHubPicker.java b/src/com/android/settings/communal/WhenToStartHubPicker.java new file mode 100644 index 00000000000..59233257a30 --- /dev/null +++ b/src/com/android/settings/communal/WhenToStartHubPicker.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2025 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.communal; + +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_CHARGING; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_DOCKED; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_NEVER; +import static android.provider.Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settings.R; +import com.android.settings.widget.RadioButtonPickerFragment; +import com.android.settingslib.widget.CandidateInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Fragment that provides radio buttons to allow the user to choose when the hub should auto-start. + */ +public class WhenToStartHubPicker extends RadioButtonPickerFragment { + private static final String TAG = "WhenToStartHubPicker"; + private static final String SHOW_WHILE_CHARGING = "while_charging"; + private static final String SHOW_WHILE_DOCKED = "while_docked"; + private static final String SHOW_WHILE_CHARGING_AND_UPRIGHT = "while_charging_and_upright"; + private static final String SHOW_NEVER = "never"; + + private Context mContext; + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + + mContext = context; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.when_to_start_hubmode_settings; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.WHEN_TO_SHOW_WIDGETS_ON_LOCKSCREEN; + } + + @Override + protected List getCandidates() { + final List candidates = new ArrayList<>(); + + final String[] entries = entries(); + final String[] values = keys(); + + if (entries == null || entries.length <= 0) return candidates; + if (values == null || values.length != entries.length) { + throw new IllegalArgumentException("Entries and values must be of the same length."); + } + + for (int i = 0; i < entries.length; i++) { + candidates.add(new WhenToStartHubCandidateInfo(entries[i], values[i])); + } + + return candidates; + } + + private String[] entries() { + return getResources().getStringArray(R.array.when_to_start_hubmode_entries); + } + + private String[] keys() { + return getResources().getStringArray(R.array.when_to_start_hubmode_values); + } + + @Override + protected String getDefaultKey() { + final int defaultValue = mContext.getResources().getInteger( + com.android.internal.R.integer.config_whenToStartHubModeDefault); + final int setting = Settings.Secure.getInt( + mContext.getContentResolver(), WHEN_TO_START_GLANCEABLE_HUB, defaultValue); + return getKeyFromSetting(setting); + } + + @Override + protected boolean setDefaultKey(String key) { + Settings.Secure.putInt( + mContext.getContentResolver(), + WHEN_TO_START_GLANCEABLE_HUB, + getSettingFromPrefKey(key)); + + return true; + } + + @Override + protected void onSelectionPerformed(boolean success) { + super.onSelectionPerformed(success); + + getActivity().finish(); + } + + + @Settings.Secure.WhenToStartGlanceableHub + private static int getSettingFromPrefKey(String key) { + switch (key) { + case SHOW_WHILE_CHARGING: + return GLANCEABLE_HUB_START_CHARGING; + case SHOW_WHILE_DOCKED: + return GLANCEABLE_HUB_START_DOCKED; + case SHOW_WHILE_CHARGING_AND_UPRIGHT: + return GLANCEABLE_HUB_START_CHARGING_UPRIGHT; + case SHOW_NEVER: + default: + return GLANCEABLE_HUB_START_NEVER; + } + } + + private static String getKeyFromSetting(@Settings.Secure.WhenToStartGlanceableHub int setting) { + switch (setting) { + case GLANCEABLE_HUB_START_CHARGING: + return SHOW_WHILE_CHARGING; + case GLANCEABLE_HUB_START_DOCKED: + return SHOW_WHILE_DOCKED; + case GLANCEABLE_HUB_START_CHARGING_UPRIGHT: + return SHOW_WHILE_CHARGING_AND_UPRIGHT; + case GLANCEABLE_HUB_START_NEVER: + default: + return SHOW_NEVER; + } + } + + private static final class WhenToStartHubCandidateInfo extends CandidateInfo { + private final String mName; + private final String mKey; + + WhenToStartHubCandidateInfo(String title, String value) { + super(true); + + mName = title; + mKey = value; + } + + @Override + public CharSequence loadLabel() { + return mName; + } + + @Override + @Nullable + public Drawable loadIcon() { + return null; + } + + @Override + public String getKey() { + return mKey; + } + } +} diff --git a/src/com/android/settings/communal/WhenToStartHubPreferenceController.java b/src/com/android/settings/communal/WhenToStartHubPreferenceController.java new file mode 100644 index 00000000000..67931ee7f65 --- /dev/null +++ b/src/com/android/settings/communal/WhenToStartHubPreferenceController.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2025 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.communal; + +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_CHARGING; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_DOCKED; +import static android.provider.Settings.Secure.GLANCEABLE_HUB_START_NEVER; +import static android.provider.Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB; + +import android.annotation.StringRes; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; + +/** + * A preference controller that is responsible for showing the "when to auto start hub" setting in + * hub settings. + */ +public class WhenToStartHubPreferenceController extends BasePreferenceController implements + PreferenceControllerMixin { + public WhenToStartHubPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + + preference.setSummary(getSummaryResId()); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public CharSequence getSummary() { + return mContext.getString(getSummaryResId()); + } + + @StringRes + private int getSummaryResId() { + final int setting = Settings.Secure.getInt( + mContext.getContentResolver(), + WHEN_TO_START_GLANCEABLE_HUB, + GLANCEABLE_HUB_START_NEVER); + + switch (setting) { + case GLANCEABLE_HUB_START_CHARGING: + return R.string.when_to_show_hubmode_charging; + case GLANCEABLE_HUB_START_DOCKED: + return R.string.when_to_show_hubmode_docked; + case GLANCEABLE_HUB_START_CHARGING_UPRIGHT: + return R.string.when_to_show_hubmode_charging_and_upright; + case GLANCEABLE_HUB_START_NEVER: + default: + return R.string.when_to_show_hubmode_never; + } + } +} diff --git a/src/com/android/settings/communal/WidgetsOnLockscreenFragment.java b/src/com/android/settings/communal/WidgetsOnLockscreenFragment.java new file mode 100644 index 00000000000..09a1e4d1c3e --- /dev/null +++ b/src/com/android/settings/communal/WidgetsOnLockscreenFragment.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 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.communal; + +import android.app.settings.SettingsEnums; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** + * Fragment that contains settings related to communal hub. + */ +@SearchIndexable +public class WidgetsOnLockscreenFragment extends DashboardFragment { + private static final String TAG = "WidgetsOnLockscreenFragment"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.WIDGETS_ON_LOCK_SCREEN; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.widgets_on_lockscreen_settings; + } + + @Override + protected String getLogTag() { + return TAG; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.widgets_on_lockscreen_settings); +} diff --git a/src/com/android/settings/display/WidgetsOnLockscreenPreferenceController.java b/src/com/android/settings/display/WidgetsOnLockscreenPreferenceController.java new file mode 100644 index 00000000000..32ba6ec5c23 --- /dev/null +++ b/src/com/android/settings/display/WidgetsOnLockscreenPreferenceController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 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; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; + +/** Controls the "widgets on lock screen" preferences (under "Display & touch"). */ +public class WidgetsOnLockscreenPreferenceController extends BasePreferenceController { + public WidgetsOnLockscreenPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public int getAvailabilityStatus() { + return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + /** + * Returns whether "widgets on lock screen" preferences are available. + */ + public static boolean isAvailable(Context context) { + if (!isMainUser(context)) { + return false; + } + + return com.android.systemui.Flags.glanceableHubV2() + && (context.getResources().getBoolean(R.bool.config_show_communal_settings) + || context.getResources().getBoolean( + R.bool.config_show_communal_settings_mobile)); + } + + private static boolean isMainUser(Context context) { + final UserManager userManager = context.getSystemService(UserManager.class); + return userManager.getUserInfo(UserHandle.myUserId()).isMain(); + } +} diff --git a/tests/robotests/src/com/android/settings/communal/CommunalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/communal/CommunalPreferenceControllerTest.java index cc970eb6b1d..17a51794ebc 100644 --- a/tests/robotests/src/com/android/settings/communal/CommunalPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/communal/CommunalPreferenceControllerTest.java @@ -16,11 +16,8 @@ package com.android.settings.communal; -import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2; -import static com.google.common.truth.Truth.assertThat; - import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -66,6 +63,7 @@ public class CommunalPreferenceControllerTest { } @Test + @DisableFlags(FLAG_GLANCEABLE_HUB_V2) public void isAvailable_communalEnabled_shouldBeTrueForPrimaryUser() { setCommunalEnabled(true); mShadowUserManager.setUserForeground(true); @@ -73,6 +71,7 @@ public class CommunalPreferenceControllerTest { } @Test + @DisableFlags(FLAG_GLANCEABLE_HUB_V2) public void isAvailable_communalEnabled_shouldBeFalseForSecondaryUser() { setCommunalEnabled(true); mShadowUserManager.setUserForeground(false); @@ -80,6 +79,7 @@ public class CommunalPreferenceControllerTest { } @Test + @DisableFlags(FLAG_GLANCEABLE_HUB_V2) public void isAvailable_communalDisabled_shouldBeFalseForPrimaryUser() { setCommunalEnabled(false); mShadowUserManager.setUserForeground(true); @@ -88,36 +88,8 @@ public class CommunalPreferenceControllerTest { @Test @EnableFlags(FLAG_GLANCEABLE_HUB_V2) - public void isAvailable_communalOnMobileEnabled_shouldBeTrueForPrimaryUser() { - setCommunalEnabled(false); - setCommunalOnMobileEnabled(true); - mShadowUserManager.setUserForeground(true); - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @Test - @EnableFlags(FLAG_GLANCEABLE_HUB_V2) - public void isAvailable_communalOnMobileEnabled_shouldBeFalseForSecondaryUser() { - setCommunalEnabled(false); - setCommunalOnMobileEnabled(true); - mShadowUserManager.setUserForeground(false); - assertFalse(mController.isAvailable()); - } - - @Test - @EnableFlags(FLAG_GLANCEABLE_HUB_V2) - public void isAvailable_communalOnMobileDisabled_shouldBeFalseForPrimaryUser() { - setCommunalEnabled(false); - setCommunalOnMobileEnabled(false); - mShadowUserManager.setUserForeground(true); - assertFalse(mController.isAvailable()); - } - - @Test - @DisableFlags(FLAG_GLANCEABLE_HUB_V2) - public void isAvailable_glanceableHubV2FlagDisabled_shouldBeFalseForPrimaryUser() { - setCommunalEnabled(false); - setCommunalOnMobileEnabled(true); + public void isAvailable_glanceableHubV2Enabled_shouldBeFalseForPrimaryUser() { + setCommunalEnabled(true); mShadowUserManager.setUserForeground(true); assertFalse(mController.isAvailable()); } @@ -125,9 +97,4 @@ public class CommunalPreferenceControllerTest { private void setCommunalEnabled(boolean enabled) { SettingsShadowResources.overrideResource(R.bool.config_show_communal_settings, enabled); } - - private void setCommunalOnMobileEnabled(boolean enabled) { - SettingsShadowResources.overrideResource( - R.bool.config_show_communal_settings_mobile, enabled); - } }