diff --git a/res/values/themes.xml b/res/values/themes.xml
index 63944d9bb34..dd711943940 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -170,6 +170,7 @@
- @*android:color/primary_device_default_settings_light
- @*android:color/primary_dark_device_default_settings_light
- @*android:color/accent_device_default_light
+ - @style/PreferenceTheme
diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml
new file mode 100644
index 00000000000..7aed02158cc
--- /dev/null
+++ b/res/xml/top_level_settings.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
index 199331d5b21..c85aad76b79 100644
--- a/src/com/android/settings/dashboard/SummaryLoader.java
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -42,6 +42,12 @@ import com.android.settingslib.utils.ThreadUtils;
import java.lang.reflect.Field;
import java.util.List;
+/**
+ * TODO(b/110405144): Remove this when all top level settings are converted to PreferenceControllers
+ *
+ * @deprecated
+ */
+@Deprecated
public class SummaryLoader {
private static final boolean DEBUG = DashboardSummary.DEBUG;
private static final String TAG = "SummaryLoader";
diff --git a/src/com/android/settings/homepage/HomepageFragment.java b/src/com/android/settings/homepage/HomepageFragment.java
index ff89dd5a4dc..2e22a035d9b 100644
--- a/src/com/android/settings/homepage/HomepageFragment.java
+++ b/src/com/android/settings/homepage/HomepageFragment.java
@@ -26,6 +26,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsHomepageActivity;
@@ -39,8 +41,6 @@ import com.google.android.material.bottomappbar.BottomAppBar;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import androidx.annotation.NonNull;
-
public class HomepageFragment extends InstrumentedFragment {
private static final String TAG = "HomepageFragment";
@@ -70,7 +70,7 @@ public class HomepageFragment extends InstrumentedFragment {
private void setupBottomBar() {
final Activity activity = getActivity();
- mSearchButton = (FloatingActionButton) activity.findViewById(R.id.search_fab);
+ mSearchButton = activity.findViewById(R.id.search_fab);
mSearchButton.setOnClickListener(v -> {
final Intent intent = SearchFeatureProvider.SEARCH_UI_INTENT;
@@ -79,7 +79,7 @@ public class HomepageFragment extends InstrumentedFragment {
startActivityForResult(intent, 0 /* requestCode */);
});
mBottomSheetBehavior = BottomSheetBehavior.from(activity.findViewById(R.id.bottom_sheet));
- final BottomAppBar bottomBar = (BottomAppBar) activity.findViewById(R.id.bar);
+ final BottomAppBar bottomBar = activity.findViewById(R.id.bar);
bottomBar.setOnClickListener(v -> {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
@@ -87,7 +87,7 @@ public class HomepageFragment extends InstrumentedFragment {
final int screenWidthpx = getResources().getDisplayMetrics().widthPixels;
final View searchbar = activity.findViewById(R.id.search_bar_container);
final View bottombar = activity.findViewById(R.id.bar);
- final Toolbar searchActionBar = (Toolbar) activity.findViewById(R.id.search_action_bar);
+ final Toolbar searchActionBar = activity.findViewById(R.id.search_action_bar);
searchActionBar.setNavigationIcon(R.drawable.ic_search_floating_24dp);
@@ -95,6 +95,7 @@ public class HomepageFragment extends InstrumentedFragment {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (!mBottomFragmentLoaded) {
+ // TODO(b/110405144): Switch to {@link TopLevelSettings} when it's ready.
SettingsHomepageActivity.switchToFragment(getActivity(),
R.id.bottom_sheet_fragment, DashboardSummary.class.getName());
mBottomFragmentLoaded = true;
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
new file mode 100644
index 00000000000..5c682cc12df
--- /dev/null
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage;
+
+import static com.android.settings.search.actionbar.SearchMenuController
+ .NEED_SEARCH_ICON_IN_ACTION_BAR;
+import static com.android.settingslib.search.SearchIndexable.MOBILE;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.instrumentation.Instrumentable;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SearchIndexable(forTarget = MOBILE)
+public class TopLevelSettings extends DashboardFragment implements
+ PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+
+ private static final String TAG = "TopLevelSettings";
+
+ public TopLevelSettings() {
+ final Bundle args = new Bundle();
+ // Disable the search icon because this page uses a full search view in actionbar.
+ args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);
+ setArguments(args);
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.top_level_settings;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DASHBOARD_SUMMARY;
+ }
+
+ @Override
+ public int getHelpResource() {
+ // Disable the help icon because this page uses a full search view in actionbar.
+ return 0;
+ }
+
+ @Override
+ public Fragment getCallbackFragment() {
+ return this;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
+ new SubSettingLauncher(getActivity())
+ .setDestination(pref.getFragment())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(caller instanceof Instrumentable
+ ? ((Instrumentable) caller).getMetricsCategory()
+ : Instrumentable.METRICS_CATEGORY_UNKNOWN)
+ .setTitleRes(-1)
+ .launch();
+ return true;
+ }
+
+ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ final SearchIndexableResource sir = new SearchIndexableResource(context);
+ sir.xmlResId = R.xml.top_level_settings;
+ return Arrays.asList(sir);
+ }
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ // Never searchable, all entries in this page are already indexed elsewhere.
+ return false;
+ }
+ };
+}
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index d5ef9aa0503..fbb0b20a543 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -149,6 +149,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements
return 0;
}
+ // TODO(b/110405144): Remove SummaryProvider
@VisibleForTesting
static class SummaryProvider implements SummaryLoader.SummaryProvider {
diff --git a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
new file mode 100644
index 00000000000..567e52e596f
--- /dev/null
+++ b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.content.Context;
+import android.icu.text.ListFormatter;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TopLevelNetworkEntryPreferenceController extends BasePreferenceController {
+
+ private final WifiMasterSwitchPreferenceController mWifiPreferenceController;
+ private final MobileNetworkPreferenceController mMobileNetworkPreferenceController;
+ private final TetherPreferenceController mTetherPreferenceController;
+
+ public TopLevelNetworkEntryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mMobileNetworkPreferenceController = new MobileNetworkPreferenceController(mContext);
+ mTetherPreferenceController = new TetherPreferenceController(
+ mContext, null /* lifecycle */);
+ mWifiPreferenceController = new WifiMasterSwitchPreferenceController(
+ mContext, null /* metrics */);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final String wifiSummary = BidiFormatter.getInstance()
+ .unicodeWrap(mContext.getString(R.string.wifi_settings_title));
+ final String mobileSummary = mContext.getString(
+ R.string.network_dashboard_summary_mobile);
+ final String dataUsageSummary = mContext.getString(
+ R.string.network_dashboard_summary_data_usage);
+ final String hotspotSummary = mContext.getString(
+ R.string.network_dashboard_summary_hotspot);
+
+ final List summaries = new ArrayList<>();
+ if (mWifiPreferenceController.isAvailable()
+ && !TextUtils.isEmpty(wifiSummary)) {
+ summaries.add(wifiSummary);
+ }
+ if (mMobileNetworkPreferenceController.isAvailable() && !TextUtils.isEmpty(mobileSummary)) {
+ summaries.add(mobileSummary);
+ }
+ if (!TextUtils.isEmpty(dataUsageSummary)) {
+ summaries.add(dataUsageSummary);
+ }
+ if (mTetherPreferenceController.isAvailable()
+ && !TextUtils.isEmpty(hotspotSummary)) {
+ summaries.add(hotspotSummary);
+ }
+ return ListFormatter.getInstance().format(summaries);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
new file mode 100644
index 00000000000..22aaf2a8aaf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils;
+import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = ShadowRestrictedLockUtils.class)
+public class TopLevelNetworkEntryPreferenceControllerTest {
+
+ @Mock
+ private WifiMasterSwitchPreferenceController mWifiPreferenceController;
+ @Mock
+ private MobileNetworkPreferenceController mMobileNetworkPreferenceController;
+ @Mock
+ private TetherPreferenceController mTetherPreferenceController;
+
+ private Context mContext;
+ private TopLevelNetworkEntryPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new TopLevelNetworkEntryPreferenceController(mContext, "test_key");
+
+ ReflectionHelpers.setField(mController, "mWifiPreferenceController",
+ mWifiPreferenceController);
+ ReflectionHelpers.setField(mController, "mMobileNetworkPreferenceController",
+ mMobileNetworkPreferenceController);
+ ReflectionHelpers.setField(mController, "mTetherPreferenceController",
+ mTetherPreferenceController);
+
+ }
+
+ @Test
+ public void getSummary_hasMobileAndHotspot_shouldReturnMobileSummary() {
+ when(mWifiPreferenceController.isAvailable()).thenReturn(true);
+ when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(true);
+ when(mTetherPreferenceController.isAvailable()).thenReturn(true);
+
+ assertThat(mController.getSummary())
+ .isEqualTo("Wi\u2011Fi, mobile, data usage, and hotspot");
+ }
+
+ @Test
+ public void getSummary_noMobileOrHotspot_shouldReturnSimpleSummary() {
+ when(mWifiPreferenceController.isAvailable()).thenReturn(true);
+ when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(false);
+ when(mTetherPreferenceController.isAvailable()).thenReturn(false);
+
+ assertThat(mController.getSummary())
+ .isEqualTo("Wi\u2011Fi and data usage");
+ }
+}