[Dynamic Home] Make a new page for all top level settings

Since we are moving conditionals/suggestions to a different place, there
is no need to use DashboardSummary to display top level settings any
more. We can simplify a lot of code for top level settings and reduce it
to a standard DashboardFragment.

- Create a new DashboardFragment + xml for all top level internal items
- Add a PreferenceController to provide summary for Network & internet
  item.
- Mark a bunch of things deprecated for future work.

Bug: 110405144
Test: robotests
Change-Id: I9f778777131c28eb836b722e089e026a59f5ddc6
This commit is contained in:
Fan Zhang
2018-08-07 18:06:39 -07:00
parent 9980096d33
commit 25d9f3812b
8 changed files with 397 additions and 5 deletions

View File

@@ -170,6 +170,7 @@
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
<item name="colorPrimaryDark">@*android:color/primary_dark_device_default_settings_light</item>
<item name="colorAccent">@*android:color/accent_device_default_light</item>
<item name="preferenceTheme">@style/PreferenceTheme</item>
</style>
<!--TODO(b/111875856) This theme will be useless, when we add real activity/fragment to handle the full screen for WifiDialog -->

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="top_level_settings"
android:title="@string/settings_label_launcher">
<Preference
android:key="top_level_network"
android:title="@string/network_dashboard_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_network"
android:fragment="com.android.settings.network.NetworkDashboardFragment"
settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
<Preference
android:key="top_level_connected_devices"
android:title="@string/connected_devices_dashboard_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_connected_device"
android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"/>
<Preference
android:key="top_level_apps_and_notifs"
android:title="@string/app_and_notification_dashboard_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_apps"
android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
<Preference
android:key="top_level_battery"
android:title="@string/power_usage_summary_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_battery"
android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"/>
<Preference
android:key="top_level_display"
android:title="@string/display_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_display"
android:fragment="com.android.settings.DisplaySettings"/>
<Preference
android:key="top_level_sound"
android:title="@string/sound_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_sound"
android:fragment="com.android.settings.notification.SoundSettings"/>
<Preference
android:key="top_level_storage"
android:title="@string/storage_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_storage"
android:fragment="com.android.settings.deviceinfo.StorageSettings"/>
<Preference
android:key="top_level_security"
android:title="@string/security_settings_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_security"
android:fragment="com.android.settings.security.SecuritySettings"/>
<Preference
android:key="top_level_accounts"
android:title="@string/account_dashboard_title"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_accounts"
android:fragment="com.android.settings.accounts.AccountDashboardFragment"/>
<Preference
android:key="top_level_accessibility"
android:title="@string/accessibility_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_accessibility"
android:fragment="com.android.settings.accessibility.AccessibilitySettings"/>
<Preference
android:key="top_level_system"
android:title="@string/header_category_system"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_system_dashboard"
android:fragment="com.android.settings.system.SystemDashboardFragment"/>
<Preference
android:key="top_level_support"
android:summary="@string/support_summary"
android:title="@string/page_tab_title_support"
android:icon="@drawable/ic_homepage_support"/>
</PreferenceScreen>

View File

@@ -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";

View File

@@ -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;

View File

@@ -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<SearchIndexableResource> 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;
}
};
}

View File

@@ -149,6 +149,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements
return 0;
}
// TODO(b/110405144): Remove SummaryProvider
@VisibleForTesting
static class SummaryProvider implements SummaryLoader.SummaryProvider {

View File

@@ -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<String> 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);
}
}

View File

@@ -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");
}
}