From 9dc9c6174c4379631e2a7750cc5809d08bec6a8a Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Fri, 11 Nov 2016 13:23:21 -0800 Subject: [PATCH] Insert dynamic dashboard tiles into optional placeholders Insteads of inserting tile onto screen using absolute priority values, now each page can have a placeholder preference, and at run time we will add dynamic dashboard tiles to placeholder's place. Bug: 32827787 Test: RunSettingsRoboTests Change-Id: I1fe9e11dce4eb6fb4a9b56af05a2b8e5cdae00d2 --- res/xml/app_and_notification.xml | 4 + res/xml/connected_devices.xml | 14 ++-- .../android/settings/SecuritySettings.java | 4 +- .../dashboard/DashboardFeatureProvider.java | 6 +- .../DashboardFeatureProviderImpl.java | 15 +++- .../settings/dashboard/DashboardFragment.java | 15 ++-- ...rdTilePlaceholderPreferenceController.java | 68 +++++++++++++++++ .../DashboardFeatureProviderImplTest.java | 25 ++++++- .../dashboard/DashboardFragmentTest.java | 27 ++++--- ...lePlaceholderPreferenceControllerTest.java | 74 +++++++++++++++++++ 10 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceControllerTest.java diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml index 4aa109e4e8c..10d81dfde99 100644 --- a/res/xml/app_and_notification.xml +++ b/res/xml/app_and_notification.xml @@ -27,4 +27,8 @@ + + \ No newline at end of file diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index e758702dc91..bf40f9d38ec 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -20,21 +20,25 @@ android:key="toggle_nfc" android:title="@string/nfc_quick_toggle_title" android:summary="@string/nfc_quick_toggle_summary" - android:order="-5" /> + android:order="-5"/> + android:order="-4"/> + android:order="-3"> + android:targetPackage="com.android.settings" + android:targetClass="com.android.settings.deviceinfo.UsbModeChooserActivity"/> + + \ No newline at end of file diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index b029662fb4e..c42b5246feb 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -781,8 +781,8 @@ public class SecuritySettings extends SettingsPreferenceFragment final List preferences = new ArrayList<>(); for (Tile tile : tiles) { final Preference pref = new Preference(getPrefContext()); - mDashboardFeatureProvider - .bindPreferenceToTile(getActivity(), pref, tile, null /* key */); + mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), pref, tile, + null /* key */, Preference.DEFAULT_ORDER/* baseOrder */); preferences.add(pref); } return preferences; diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java index 8601f1d73ff..60d4abe9ccb 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java @@ -63,12 +63,16 @@ public interface DashboardFeatureProvider { * @param pref The preference to bind data * @param tile The binding data * @param key They key for preference. If null, we will generate one from tile data + * @param baseOrder The order offset value. When binding, pref's order is determined by + * both this value and tile's own priority. */ - void bindPreferenceToTile(Activity activity, Preference pref, Tile tile, String key); + void bindPreferenceToTile(Activity activity, Preference pref, Tile tile, String key, + int baseOrder); /** * Returns a {@link ProgressiveDisclosureMixin} for specified fragment. */ ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context, DashboardFragment fragment); + } diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index d07d3030111..ef1265d4f4d 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -80,7 +80,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } @Override - public void bindPreferenceToTile(Activity activity, Preference pref, Tile tile, String key) { + public void bindPreferenceToTile(Activity activity, Preference pref, Tile tile, String key, + int baseOrder) { pref.setTitle(tile.title); if (!TextUtils.isEmpty(key)) { pref.setKey(key); @@ -112,11 +113,21 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { return true; }); } + final String skipOffsetPackageName = activity.getPackageName(); // Use negated priority for order, because tile priority is based on intent-filter // (larger value has higher priority). However pref order defines smaller value has // higher priority. if (tile.priority != 0) { - pref.setOrder(-tile.priority); + boolean shouldSkipBaseOrderOffset = false; + if (tile.intent != null) { + shouldSkipBaseOrderOffset = TextUtils.equals( + skipOffsetPackageName, tile.intent.getComponent().getPackageName()); + } + if (shouldSkipBaseOrderOffset || baseOrder == Preference.DEFAULT_ORDER) { + pref.setOrder(-tile.priority); + } else { + pref.setOrder(-tile.priority + baseOrder); + } } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index dddc4cc1dfe..b0f87341758 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -62,6 +62,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment protected ProgressiveDisclosureMixin mProgressiveDisclosureMixin; protected DashboardFeatureProvider mDashboardFeatureProvider; + private DashboardTilePlaceholderPreferenceController mPlaceholderPreferenceController; private boolean mListeningToCategoryChange; private SummaryLoader mSummaryLoader; @@ -74,10 +75,13 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment .getProgressiveDisclosureMixin(context, this); getLifecycle().addObserver(mProgressiveDisclosureMixin); - final List controllers = getPreferenceControllers(context); + List controllers = getPreferenceControllers(context); if (controllers == null) { - return; + controllers = new ArrayList<>(); } + mPlaceholderPreferenceController = + new DashboardTilePlaceholderPreferenceController(context); + controllers.add(mPlaceholderPreferenceController); for (PreferenceController controller : controllers) { addPreferenceController(controller); } @@ -319,12 +323,13 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment // Have the key already, will rebind. final Preference preference = mProgressiveDisclosureMixin.findPreference( screen, key); - mDashboardFeatureProvider.bindPreferenceToTile( - getActivity(), preference, tile, key); + mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), preference, tile, key, + mPlaceholderPreferenceController.getOrder()); } else { // Don't have this key, add it. final Preference pref = new Preference(getPrefContext()); - mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), pref, tile, key); + mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), pref, tile, key, + mPlaceholderPreferenceController.getOrder()); mProgressiveDisclosureMixin.addPreference(screen, pref); mDashboardTilePrefKeys.add(key); } diff --git a/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceController.java b/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceController.java new file mode 100644 index 00000000000..6b0018c24fe --- /dev/null +++ b/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceController.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 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.dashboard; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceController; + +/** + * PreferenceController for a dashboard_tile_placeholder, a special preference marking where + * dynamic dashboard tiles should be injected in a screen. It is optional when building + * preference screen in xml. If not present, all dynamic dashboard tiles will be added to the + * bottom of page. + */ +class DashboardTilePlaceholderPreferenceController extends PreferenceController { + + private static final String KEY_PLACEHOLDER = "dashboard_tile_placeholder"; + + private int mOrder = Preference.DEFAULT_ORDER; + + public DashboardTilePlaceholderPreferenceController(Context context) { + super(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + final Preference pref = screen.findPreference(getPreferenceKey()); + if (pref != null) { + mOrder = pref.getOrder(); + screen.removePreference(pref); + } + } + + @Override + public boolean isAvailable() { + return false; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + return false; + } + + @Override + public String getPreferenceKey() { + return KEY_PLACEHOLDER; + } + + public int getOrder() { + return mOrder; + } +} diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index deae989e565..fabb438d00c 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -77,7 +77,7 @@ public class DashboardFeatureProviderImplTest { tile.metaData = new Bundle(); tile.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); tile.priority = 10; - mImpl.bindPreferenceToTile(mActivity, preference, tile, "123"); + mImpl.bindPreferenceToTile(mActivity, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(tile.title); assertThat(preference.getSummary()).isEqualTo(tile.summary); @@ -95,7 +95,9 @@ public class DashboardFeatureProviderImplTest { tile.metaData = new Bundle(); tile.priority = 10; tile.intent = new Intent(); - mImpl.bindPreferenceToTile(mActivity, preference, tile, "123"); + tile.intent.setComponent(new ComponentName("pkg", "class")); + + mImpl.bindPreferenceToTile(mActivity, preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getFragment()).isNull(); assertThat(preference.getOnPreferenceClickListener()).isNotNull(); @@ -112,11 +114,12 @@ public class DashboardFeatureProviderImplTest { tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class)); tile.intent = new Intent(); + tile.intent.setComponent(new ComponentName("pkg", "class")); when(mActivity.getApplicationContext().getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); - mImpl.bindPreferenceToTile(mActivity, preference, tile, "123"); + mImpl.bindPreferenceToTile(mActivity, preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mActivity).getFragmentManager(); @@ -129,9 +132,23 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new Tile(); tile.intent = new Intent(); tile.intent.setComponent(new ComponentName("pkg", "class")); - mImpl.bindPreferenceToTile(mActivity, preference, tile, null /* key */); + mImpl.bindPreferenceToTile(mActivity, preference, tile, null /* key */ + , Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isNotNull(); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); } + + @Test + public void bindPreference_withBaseOrder_shouldOffsetPriority() { + final int baseOrder = 100; + final Preference preference = new Preference( + ShadowApplication.getInstance().getApplicationContext()); + final Tile tile = new Tile(); + tile.metaData = new Bundle(); + tile.priority = 10; + mImpl.bindPreferenceToTile(mActivity, preference, tile, "123", baseOrder); + + assertThat(preference.getOrder()).isEqualTo(-tile.priority + baseOrder); + } } diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java index 7c49749556d..f4772020bff 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java @@ -42,9 +42,6 @@ import org.robolectric.shadows.ShadowApplication; import java.util.ArrayList; import java.util.List; -import java.util.ArrayList; -import java.util.List; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; @@ -122,19 +119,29 @@ public class DashboardFragmentTest { verify(mTestFragment.mScreen, never()).addPreference(any(Preference.class)); } + @Test + public void onAttach_shouldCreatePlaceholderPreferenceController() { + final PreferenceController controller = mTestFragment.getPreferenceController( + DashboardTilePlaceholderPreferenceController.class); + + assertThat(controller).isNotNull(); + } + @Test public void updateState_skipUnavailablePrefs() { - List preferenceControllers = mTestFragment.mControllers; - preferenceControllers.add(mock(PreferenceController.class)); - preferenceControllers.add(mock(PreferenceController.class)); - when(preferenceControllers.get(0).isAvailable()).thenReturn(false); - when(preferenceControllers.get(1).isAvailable()).thenReturn(true); + final List preferenceControllers = mTestFragment.mControllers; + final PreferenceController mockController1 = mock(PreferenceController.class); + final PreferenceController mockController2 = mock(PreferenceController.class); + preferenceControllers.add(mockController1); + preferenceControllers.add(mockController2); + when(mockController1.isAvailable()).thenReturn(false); + when(mockController2.isAvailable()).thenReturn(true); mTestFragment.onAttach(ShadowApplication.getInstance().getApplicationContext()); mTestFragment.onResume(); - verify(mTestFragment.mControllers.get(0), never()).getPreferenceKey(); - verify(mTestFragment.mControllers.get(1)).getPreferenceKey(); + verify(mockController1, never()).getPreferenceKey(); + verify(mockController2).getPreferenceKey(); } public static class TestPreferenceController extends PreferenceController { diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceControllerTest.java new file mode 100644 index 00000000000..a7b0c5420c6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardTilePlaceholderPreferenceControllerTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 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.dashboard; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class DashboardTilePlaceholderPreferenceControllerTest { + + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + private DashboardTilePlaceholderPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new DashboardTilePlaceholderPreferenceController(mContext); + } + + @Test + public void display_hasPlaceholderPref_shouldUseOrderFromPlaceholder() { + final int baseOrder = 15; + when(mScreen.findPreference(anyString()).getOrder()).thenReturn(baseOrder); + + mController.displayPreference(mScreen); + + assertThat(mController.getOrder()).isEqualTo(baseOrder); + } + + @Test + public void display_noPlaceholderPref_shouldUseDefaultOrder() { + when(mScreen.findPreference(anyString())).thenReturn(null); + + mController.displayPreference(mScreen); + + assertThat(mController.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); + } +}