From 29a09e55ba8596a92c51e222c0736706856b3bb1 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Tue, 12 Oct 2021 17:24:34 +0800 Subject: [PATCH] Revert "Revert "Support highlightable Settings homepage menu for 2-pane"" This reverts commit 096c090b805acbed1ea5a45f584ff5a24bd2fa3b. Reason for revert: crash is fixed in this change Bug: 202510128 Test: manual, build Change-Id: Ifff8d58cfdcf43e123b11dee308665b2617cb4b3 --- AndroidManifest.xml | 132 +++++++++- src/com/android/settings/Settings.java | 11 +- .../android/settings/SettingsActivity.java | 25 +- .../settings/SettingsPreferenceFragment.java | 3 +- src/com/android/settings/Utils.java | 8 + .../ActivityEmbeddingUtils.java | 8 + .../settings/dashboard/CategoryManager.java | 16 ++ .../dashboard/DashboardFeatureProvider.java | 4 +- .../DashboardFeatureProviderImpl.java | 24 +- .../settings/dashboard/DashboardFragment.java | 6 +- .../profileselector/ProfileSelectDialog.java | 14 +- .../settings/homepage/HighlightableMenu.java | 117 +++++++++ .../homepage/SettingsHomepageActivity.java | 54 +++- .../settings/homepage/TopLevelSettings.java | 99 ++++++++ .../search/SearchFeatureProviderImpl.java | 2 +- ...ighlightableTopLevelPreferenceAdapter.java | 238 ++++++++++++++++++ .../AccountDetailDashboardFragmentTest.java | 7 +- .../DashboardFeatureProviderImplTest.java | 104 ++++---- 18 files changed, 781 insertions(+), 91 deletions(-) create mode 100644 src/com/android/settings/homepage/HighlightableMenu.java create mode 100644 src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d04b884f426..674ab61eb27 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -136,7 +136,7 @@ android:taskAffinity="com.android.settings.root" android:launchMode="singleTask" android:exported="true" - android:configChanges="keyboard|keyboardHidden|screenSize|screenLayout"> + android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout"> @@ -228,6 +228,8 @@ + @@ -257,6 +259,8 @@ + @@ -314,6 +318,8 @@ + @@ -479,6 +485,8 @@ + @@ -489,6 +497,8 @@ android:clearTaskOnLaunch="true"> + + + + + + + + @@ -618,6 +642,8 @@ android:targetActivity="Settings$TetherSettingsActivity"> + + + @@ -661,6 +691,8 @@ + + @@ -858,6 +892,8 @@ + @@ -911,6 +947,8 @@ + @@ -921,6 +959,8 @@ android:exported="true"> + + @@ -991,6 +1033,8 @@ android:value="com.android.settings.notification.zen.ZenModeEventRuleSettings" /> + + @@ -1028,6 +1074,8 @@ + + @@ -1065,6 +1115,8 @@ + @@ -1088,6 +1140,8 @@ android:value="true" /> + + @@ -1353,6 +1409,8 @@ + + @@ -1629,6 +1689,8 @@ + @@ -1644,6 +1706,8 @@ + @@ -1697,6 +1761,8 @@ + @@ -1715,6 +1781,8 @@ + @@ -1729,6 +1797,8 @@ + @@ -1743,6 +1813,8 @@ + @@ -1757,6 +1829,8 @@ + @@ -2429,6 +2503,8 @@ + + + + + + + + + + + + @@ -2821,6 +2919,8 @@ + @@ -2835,6 +2935,8 @@ + + @@ -2893,6 +2997,8 @@ + @@ -3301,6 +3407,8 @@ android:value="true" /> + + @@ -3362,6 +3472,8 @@ android:exported="true"> + + @@ -3394,6 +3508,8 @@ + + @@ -3577,6 +3695,8 @@ + @@ -3722,6 +3842,8 @@ + + + + @@ -3782,7 +3910,7 @@ android:permission="android.permission.BLUETOOTH_CONNECT"> - /> + = getMinCurrentScreenSplitWidthPx(appContext) + && dm.heightPixels >= getMinSmallestScreenSplitWidthPx(appContext); + } } diff --git a/src/com/android/settings/dashboard/CategoryManager.java b/src/com/android/settings/dashboard/CategoryManager.java index 2a82abe4859..b6ec4ca3269 100644 --- a/src/com/android/settings/dashboard/CategoryManager.java +++ b/src/com/android/settings/dashboard/CategoryManager.java @@ -17,6 +17,7 @@ package com.android.settings.dashboard; import android.content.ComponentName; import android.content.Context; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -24,6 +25,7 @@ import android.util.Pair; import androidx.annotation.VisibleForTesting; +import com.android.settings.homepage.HighlightableMenu; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.DashboardCategory; @@ -153,6 +155,20 @@ public class CategoryManager { filterDuplicateTiles(mCategoryByKeyMap); if (firstLoading) { logTiles(context); + + final DashboardCategory homepageCategory = mCategoryByKeyMap.get( + CategoryKey.CATEGORY_HOMEPAGE); + if (homepageCategory == null) { + return; + } + for (Tile tile : homepageCategory.getTiles()) { + final String key = tile.getKey(context); + if (TextUtils.isEmpty(key)) { + Log.w(TAG, "Key hint missing for homepage tile: " + tile.getTitle(context)); + continue; + } + HighlightableMenu.addMenuKey(key); + } } } } diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java index 8c872f0b8b0..b88ecd4240b 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java @@ -47,9 +47,9 @@ public interface DashboardFeatureProvider { * Binds preference to data provided by tile and gets dynamic data observers. * * @param activity If tile contains intent to launch, it will be launched from this activity + * @param fragment The fragment that the preference will be bound to * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to * rounded icon. - * @param sourceMetricsCategory The context (source) from which an action is performed * @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 @@ -58,7 +58,7 @@ public interface DashboardFeatureProvider { * @return The list of dynamic data observers */ List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, + DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, String key, int baseOrder); /** diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index 27b20cf4ef7..f8185db7178 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -36,6 +36,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITL import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface.OnCancelListener; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.PackageManager; @@ -60,6 +61,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.dashboard.profileselector.ProfileSelectDialog; +import com.android.settings.homepage.TopLevelSettings; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.PrimarySwitchPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -123,7 +125,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { @Override public List bindPreferenceToTileAndGetObservers(FragmentActivity activity, - boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile, + DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile, String key, int baseOrder) { if (pref == null) { return null; @@ -149,6 +151,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { bindIcon(pref, tile, forceRoundedIcon); if (tile instanceof ActivityTile) { + final int sourceMetricsCategory = fragment.getMetricsCategory(); final Bundle metadata = tile.getMetaData(); String clsName = null; String action = null; @@ -166,7 +169,17 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { intent.setAction(action); } pref.setOnPreferenceClickListener(preference -> { - launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory); + OnCancelListener listener = null; + if (fragment instanceof TopLevelSettings) { + final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment; + // Highlight the tile immediately whenever it's clicked + topLevelSettings.setHighlightPreferenceKey(key); + // If the tile allows users to select profile, the pop-op dialog may be + // cancelled and then the previous highlight entry should be restored. + listener = dialog -> topLevelSettings.restorePreviousHighlight(); + } + launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory, + listener); return true; }); } @@ -198,7 +211,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { .putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, SettingsEnums.DASHBOARD_SUMMARY) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY); + launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY, + /* listener= */ null); } private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) { @@ -413,7 +427,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent, - int sourceMetricCategory) { + int sourceMetricCategory, OnCancelListener listener) { if (!isIntentResolvable(intent)) { Log.w(TAG, "Cannot resolve intent, skipping. " + intent); return; @@ -444,7 +458,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile, - sourceMetricCategory); + sourceMetricCategory, listener); } } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index dfd931db7e9..4317fc6ef40 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -496,15 +496,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment if (mDashboardTilePrefKeys.containsKey(key)) { // Have the key already, will rebind. final Preference preference = screen.findPreference(key); - mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), - forceRoundedIcons, getMetricsCategory(), preference, tile, key, + mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), this, + forceRoundedIcons, preference, tile, key, mPlaceholderPreferenceController.getOrder()); } else { // Don't have this key, add it. final Preference pref = createPreference(tile); final List observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(), - forceRoundedIcons, getMetricsCategory(), pref, tile, key, + this, forceRoundedIcons, pref, tile, key, mPlaceholderPreferenceController.getOrder()); screen.addPreference(pref); registerDynamicDataObservers(observers); diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java index 36cdd422508..b05f23b812b 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java @@ -19,6 +19,7 @@ package com.android.settings.dashboard.profileselector; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.Bundle; @@ -44,19 +45,23 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen private int mSourceMetricCategory; private Tile mSelectedTile; + private OnCancelListener mOnCancelListener; /** * Display the profile select dialog, adding the fragment to the given FragmentManager. * @param manager The FragmentManager this fragment will be added to. * @param tile The tile for this fragment. * @param sourceMetricCategory The source metric category. + * @param listener The listener listens to the dialog cancelling event. */ - public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory) { + public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory, + OnCancelListener listener) { final ProfileSelectDialog dialog = new ProfileSelectDialog(); final Bundle args = new Bundle(); args.putParcelable(ARG_SELECTED_TILE, tile); args.putInt(ARG_SOURCE_METRIC_CATEGORY, sourceMetricCategory); dialog.setArguments(args); + dialog.mOnCancelListener = listener; dialog.show(manager, "select_profile"); } @@ -91,6 +96,13 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen getActivity().startActivityAsUser(intent, user); } + @Override + public void onCancel(DialogInterface dialog) { + if (mOnCancelListener != null) { + mOnCancelListener.onCancel(dialog); + } + } + public static void updateUserHandlesIfNeeded(Context context, Tile tile) { final List userHandles = tile.userHandle; if (tile.userHandle == null || tile.userHandle.size() <= 1) { diff --git a/src/com/android/settings/homepage/HighlightableMenu.java b/src/com/android/settings/homepage/HighlightableMenu.java new file mode 100644 index 00000000000..6a10e66dd6a --- /dev/null +++ b/src/com/android/settings/homepage/HighlightableMenu.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 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.core.PreferenceXmlParserUtils.METADATA_HIGHLIGHTABLE_MENU_KEY; +import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; + +import android.annotation.XmlRes; +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.settings.core.PreferenceXmlParserUtils; +import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Class for mapping highlightable menu keys and preference keys + */ +public class HighlightableMenu { + private static final String TAG = "HighlightableMenu"; + + /** + * Map from highlightable menu key to preference key. + */ + private static final Map MENU_TO_PREFERENCE_KEY_MAP; + + /** + * Map from old menu key to current key string id. + */ + private static final Map MENU_KEY_COMPAT_MAP; + + private static boolean sXmlParsed; + + static { + MENU_TO_PREFERENCE_KEY_MAP = new ArrayMap<>(); + MENU_KEY_COMPAT_MAP = new ArrayMap<>(); + + // Manual mapping for platform compatibility, e.g. + // MENU_KEY_COMPAT_MAP.put("top_level_apps_and_notifs", R.string.menu_key_apps); + } + + /** Parses the highlightable menu keys from xml */ + public static synchronized void fromXml(Context context, @XmlRes int xmlResId) { + if (sXmlParsed) { + return; + } + + Log.d(TAG, "parsing highlightable menu from xml"); + final List preferenceMetadata; + try { + preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId, + MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Failed to parse preference xml for getting highlightable menu keys", e); + return; + } + + for (Bundle metadata : preferenceMetadata) { + final String menuKey = metadata.getString(METADATA_HIGHLIGHTABLE_MENU_KEY); + if (TextUtils.isEmpty(menuKey)) { + continue; + } + final String prefKey = metadata.getString(METADATA_KEY); + if (TextUtils.isEmpty(prefKey)) { + Log.w(TAG, "Highlightable menu requires android:key but it's missing in xml: " + + menuKey); + continue; + } + MENU_TO_PREFERENCE_KEY_MAP.put(menuKey, prefKey); + } + + if (MENU_TO_PREFERENCE_KEY_MAP.isEmpty()) { + return; + } + + sXmlParsed = true; + MENU_KEY_COMPAT_MAP.forEach((compatMenuKey, keyId) -> { + final String prefKey = lookupPreferenceKey(context.getString(keyId)); + if (prefKey != null) { + MENU_TO_PREFERENCE_KEY_MAP.put(compatMenuKey, prefKey); + } + }); + } + + /** Manually adds a preference as the menu key for Injection */ + public static synchronized void addMenuKey(String key) { + Log.d(TAG, "add menu key: " + key); + MENU_TO_PREFERENCE_KEY_MAP.put(key, key); + } + + /** Looks up the preference key by a specified menu key */ + public static String lookupPreferenceKey(String menuKey) { + return MENU_TO_PREFERENCE_KEY_MAP.get(menuKey); + } +} diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 3e9b9d059f5..471b1a4d1e2 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -16,13 +16,15 @@ package com.android.settings.homepage; +import static android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK; +import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI; +import static android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY; + import android.animation.LayoutTransition; import android.app.ActivityManager; -import android.app.PendingIntent; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Bundle; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -36,16 +38,16 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.window.embedding.SplitController; import com.android.settings.R; import com.android.settings.Settings; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.accounts.AvatarViewMixin; -import com.android.settings.core.CategoryMixin; -import com.android.settings.core.FeatureFlags; import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.core.CategoryMixin; +import com.android.settings.core.FeatureFlags; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; @@ -64,6 +66,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements // An alias class name of SettingsHomepageActivity. public static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity"; + private static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; private View mHomepageView; @@ -120,7 +123,11 @@ public class SettingsHomepageActivity extends FragmentActivity implements showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content); } } - showFragment(new TopLevelSettings(), R.id.main_content); + final Fragment fragment = new TopLevelSettings(); + fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, + getHighlightMenuKey()); + showFragment(fragment, R.id.main_content); + ((FrameLayout) findViewById(R.id.main_content)) .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); @@ -132,9 +139,13 @@ public class SettingsHomepageActivity extends FragmentActivity implements protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - // When it's large screen 2-pane and Settings app is in background. Receiving a Intent - // in this Activity will not finish nor onCreate. setIntent here for this case. + // When it's large screen 2-pane and Settings app is in the background, receiving an Intent + // will not recreate this activity. Update the intent for this case. setIntent(intent); + reloadHighlightMenuKey(); + if (isFinishing()) { + return; + } // Launch the intent from deep link for large screen devices. launchDeepLinkIntentToRight(); } @@ -180,12 +191,12 @@ public class SettingsHomepageActivity extends FragmentActivity implements final Intent intent = getIntent(); if (intent == null || !TextUtils.equals(intent.getAction(), - android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { return; } final String intentUriString = intent.getStringExtra( - android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); + EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI); if (TextUtils.isEmpty(intentUriString)) { Log.e(TAG, "No EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI to deep link"); finish(); @@ -236,6 +247,29 @@ public class SettingsHomepageActivity extends FragmentActivity implements startActivity(targetIntent); } + private String getHighlightMenuKey() { + final Intent intent = getIntent(); + if (intent != null && TextUtils.equals(intent.getAction(), + ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) { + final String menuKey = intent.getStringExtra( + EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY); + if (!TextUtils.isEmpty(menuKey)) { + return menuKey; + } + } + return getString(DEFAULT_HIGHLIGHT_MENU_KEY); + } + + private void reloadHighlightMenuKey() { + final TopLevelSettings fragment = + (TopLevelSettings) getSupportFragmentManager().findFragmentById(R.id.main_content); + if (fragment != null) { + fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, + getHighlightMenuKey()); + fragment.reloadHighlightMenuKey(); + } + } + private void initHomepageContainer() { final View view = findViewById(R.id.homepage_container); // Prevent inner RecyclerView gets focus and invokes scrolling. diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index 681ea5111c7..a819b870540 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -21,20 +21,27 @@ import static com.android.settingslib.search.SearchIndexable.MOBILE; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceScreen; +import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.support.SupportPreferenceController; +import com.android.settings.widget.HighlightableTopLevelPreferenceAdapter; import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.search.SearchIndexable; @@ -43,6 +50,11 @@ public class TopLevelSettings extends DashboardFragment implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { private static final String TAG = "TopLevelSettings"; + private static final String SAVED_HIGHLIGHTED_PREF = "highlighted_pref"; + + private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter; + + private String mHighlightedPreferenceKey; public TopLevelSettings() { final Bundle args = new Bundle(); @@ -69,6 +81,7 @@ public class TopLevelSettings extends DashboardFragment implements @Override public void onAttach(Context context) { super.onAttach(context); + HighlightableMenu.fromXml(context, getPreferenceScreenResId()); use(SupportPreferenceController.class).setActivity(getActivity()); } @@ -83,6 +96,12 @@ public class TopLevelSettings extends DashboardFragment implements return this; } + @Override + public boolean onPreferenceTreeClick(Preference preference) { + setHighlightPreferenceKey(preference.getKey()); + return super.onPreferenceTreeClick(preference); + } + @Override public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { new SubSettingLauncher(getActivity()) @@ -96,6 +115,20 @@ public class TopLevelSettings extends DashboardFragment implements return true; } + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + if (icicle != null) { + mHighlightedPreferenceKey = icicle.getString(SAVED_HIGHLIGHTED_PREF); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_HIGHLIGHTED_PREF, mHighlightedPreferenceKey); + } + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); @@ -118,12 +151,78 @@ public class TopLevelSettings extends DashboardFragment implements } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + highlightPreferenceIfNeeded(); + } + + @Override + public void highlightPreferenceIfNeeded() { + if (mTopLevelAdapter != null) { + mTopLevelAdapter.requestHighlight(); + } + } + + /** Highlight a preference with specified key */ + public void setHighlightPreferenceKey(String prefKey) { + if (mTopLevelAdapter != null) { + mHighlightedPreferenceKey = prefKey; + mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false); + } + } + + /** Highlight the previous preference */ + public void restorePreviousHighlight() { + if (mTopLevelAdapter != null) { + mTopLevelAdapter.restorePreviousHighlight(); + } + } + @Override protected boolean shouldForceRoundedIcon() { return getContext().getResources() .getBoolean(R.bool.config_force_rounded_icon_TopLevelSettings); } + @Override + protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { + if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) { + return super.onCreateAdapter(preferenceScreen); + } + + if (TextUtils.isEmpty(mHighlightedPreferenceKey)) { + mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); + } + + Log.d(TAG, "onCreateAdapter, pref key: " + mHighlightedPreferenceKey); + mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(preferenceScreen, + getListView(), mHighlightedPreferenceKey); + return mTopLevelAdapter; + } + + void reloadHighlightMenuKey() { + if (mTopLevelAdapter == null) { + return; + } + + mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments(); + Log.d(TAG, "reloadHighlightMenuKey, pref key: " + mHighlightedPreferenceKey); + mTopLevelAdapter.highlightPreference(mHighlightedPreferenceKey, /* scrollNeeded= */ true); + } + + private String getHighlightPrefKeyFromArguments() { + final Bundle arguments = getArguments(); + final String menuKey = arguments.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY); + final String prefKey = HighlightableMenu.lookupPreferenceKey(menuKey); + if (TextUtils.isEmpty(prefKey)) { + Log.e(TAG, "Invalid highlight menu key: " + menuKey); + } else { + Log.d(TAG, "Menu key: " + menuKey); + } + return prefKey; + } + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.top_level_settings) { diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java index 508d37d7e6a..6f909709058 100644 --- a/src/com/android/settings/search/SearchFeatureProviderImpl.java +++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java @@ -50,7 +50,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { if (isSettingsPackage || isAllowlistedPackage) { return; } - throw new SecurityException("Search result intents must be called with from a " + throw new SecurityException("Search result intents must be called with from an " + "allowlisted package."); } diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java new file mode 100644 index 00000000000..eeaad214a21 --- /dev/null +++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2021 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.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceGroupAdapter; +import androidx.preference.PreferenceViewHolder; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.Utils; +import com.android.settings.activityembedding.ActivityEmbeddingUtils; + +/** + * Adapter for highlighting top level preferences + */ +public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapter { + + private static final String TAG = "HighlightableTopLevelAdapter"; + + static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L; + + @VisibleForTesting + final int mHighlightColor; + final int mTitleColorNormal; + final int mTitleColorHighlight; + final int mSummaryColorNormal; + final int mSummaryColorHighlight; + final int mIconColorNormal; + final int mIconColorHighlight; + + private final Context mContext; + private final RecyclerView mRecyclerView; + private final int mNormalBackgroundRes; + private String mHighlightKey; + private String mPreviousHighlightKey; + private int mHighlightPosition = RecyclerView.NO_POSITION; + private boolean mHighlightNeeded; + private boolean mScrolled; + + public HighlightableTopLevelPreferenceAdapter(PreferenceGroup preferenceGroup, + RecyclerView recyclerView, String key) { + super(preferenceGroup); + mRecyclerView = recyclerView; + mHighlightKey = key; + mContext = preferenceGroup.getContext(); + final TypedValue outValue = new TypedValue(); + mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true /* resolveRefs */); + mNormalBackgroundRes = outValue.resourceId; + mHighlightColor = Utils.getColorAttrDefaultColor(mContext, + com.android.internal.R.attr.colorAccentSecondaryVariant); + mTitleColorNormal = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); + mTitleColorHighlight = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimaryInverse); + mSummaryColorNormal = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondary); + mSummaryColorHighlight = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondaryInverse); + mIconColorNormal = Utils.getHomepageIconColor(mContext); + mIconColorHighlight = Utils.getHomepageIconColorHighlight(mContext); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder, int position) { + super.onBindViewHolder(holder, position); + updateBackground(holder, position); + } + + @VisibleForTesting + void updateBackground(PreferenceViewHolder holder, int position) { + if (!isHighlightNeeded()) { + removeHighlightBackground(holder); + return; + } + + if (position == mHighlightPosition + && mHighlightKey != null + && TextUtils.equals(mHighlightKey, getItem(position).getKey())) { + // This position should be highlighted. + addHighlightBackground(holder); + } else { + removeHighlightBackground(holder); + } + } + + /** + * A function can highlight a specific setting in recycler view. + */ + public void requestHighlight() { + if (mRecyclerView == null || TextUtils.isEmpty(mHighlightKey)) { + return; + } + + if (TextUtils.isEmpty(mHighlightKey)) { + // De-highlight previous preference. + final int previousPosition = mHighlightPosition; + mHighlightPosition = RecyclerView.NO_POSITION; + mScrolled = true; + if (previousPosition >= 0) { + notifyItemChanged(previousPosition); + } + return; + } + + final int position = getPreferenceAdapterPosition(mHighlightKey); + if (position < 0) { + return; + } + + final boolean highlightNeeded = isHighlightNeeded(); + if (highlightNeeded) { + scrollToPositionIfNeeded(position); + } + + // Turn on/off highlight when screen split mode is changed. + if (highlightNeeded != mHighlightNeeded) { + Log.d(TAG, "Highlight change needed: " + highlightNeeded); + mHighlightNeeded = highlightNeeded; + mHighlightPosition = position; + notifyItemChanged(position); + return; + } + + if (position == mHighlightPosition) { + return; + } + + final int previousPosition = mHighlightPosition; + mHighlightPosition = position; + Log.d(TAG, "Request highlight position " + position); + Log.d(TAG, "Is highlight needed: " + highlightNeeded); + if (!highlightNeeded) { + return; + } + + // Highlight preference. + notifyItemChanged(position); + + // De-highlight previous preference. + if (previousPosition >= 0) { + notifyItemChanged(previousPosition); + } + } + + /** + * A function that highlights a setting by specifying a preference key. Usually used whenever a + * preference is clicked. + */ + public void highlightPreference(String key, boolean scrollNeeded) { + mPreviousHighlightKey = mHighlightKey; + mHighlightKey = key; + mScrolled = !scrollNeeded; + requestHighlight(); + } + + /** + * A function that restores the previous highlighted setting. + */ + public void restorePreviousHighlight() { + mHighlightKey = mPreviousHighlightKey; + requestHighlight(); + } + + private void scrollToPositionIfNeeded(int position) { + if (mScrolled || position < 0) { + return; + } + + // Only when the recyclerView is loaded, it can be scrolled + final View view = mRecyclerView.getChildAt(position); + if (view == null) { + mRecyclerView.postDelayed(() -> scrollToPositionIfNeeded(position), + DELAY_HIGHLIGHT_DURATION_MILLIS); + return; + } + + mScrolled = true; + Log.d(TAG, "Scroll to position " + position); + // Scroll to the top to reset the position. + mRecyclerView.nestedScrollBy(0, -mRecyclerView.getHeight()); + + final int scrollY = view.getTop(); + if (scrollY > 0) { + mRecyclerView.nestedScrollBy(0, scrollY); + } + } + + private void addHighlightBackground(PreferenceViewHolder holder) { + final View v = holder.itemView; + v.setBackgroundColor(mHighlightColor); + ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorHighlight); + ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorHighlight); + final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); + if (drawable != null) { + drawable.setTint(mIconColorHighlight); + } + } + + private void removeHighlightBackground(PreferenceViewHolder holder) { + final View v = holder.itemView; + v.setBackgroundResource(mNormalBackgroundRes); + ((TextView) v.findViewById(android.R.id.title)).setTextColor(mTitleColorNormal); + ((TextView) v.findViewById(android.R.id.summary)).setTextColor(mSummaryColorNormal); + final Drawable drawable = ((ImageView) v.findViewById(android.R.id.icon)).getDrawable(); + if (drawable != null) { + drawable.setTint(mIconColorNormal); + } + } + + private boolean isHighlightNeeded() { + return ActivityEmbeddingUtils.isTwoPaneResolution(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java index a827284a088..cccca9c9ea5 100644 --- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java @@ -43,7 +43,6 @@ import android.os.UserManager; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; -import com.android.internal.logging.nano.MetricsProto; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.testutils.shadow.ShadowAccountManager; import com.android.settings.testutils.shadow.ShadowUserManager; @@ -151,9 +150,9 @@ public class AccountDetailDashboardFragmentTest { final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class); final Preference preference = new Preference(mContext); - dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, - false /* forceRoundedIcon */, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, - preference, tile, null /* key */, Preference.DEFAULT_ORDER); + dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity, mFragment, + false /* forceRoundedIcon */, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); preference.performClick(); diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index 5d6fdf98f1b..e7c99c873b1 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -114,6 +114,7 @@ public class DashboardFeatureProviderImplTest { private Bundle mSwitchMetaData; private DashboardFeatureProviderImpl mImpl; private boolean mForceRoundedIcon; + private DashboardFragment mFragment; @Before public void setUp() { @@ -144,6 +145,7 @@ public class DashboardFeatureProviderImplTest { .thenReturn(new ResolveInfo()); mFeatureFactory = FakeFeatureFactory.setupForTest(); mImpl = new DashboardFeatureProviderImpl(mContext); + mFragment = new TestFragment(); } @Test @@ -159,8 +161,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -180,8 +182,8 @@ public class DashboardFeatureProviderImplTest { doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) .when(tile).getIcon(any(Context.class)); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, preference, tile, - null /* key*/, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key*/, + Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getSummary()) @@ -198,8 +200,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getFragment()).isNull(); assertThat(preference.getOnPreferenceClickListener()).isNotNull(); @@ -214,8 +216,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mActivity).getSupportFragmentManager(); @@ -231,8 +233,8 @@ public class DashboardFeatureProviderImplTest { when(mActivity.getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( @@ -250,8 +252,8 @@ public class DashboardFeatureProviderImplTest { tile.userHandle = new ArrayList<>(); tile.userHandle.add(mock(UserHandle.class)); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.getOnPreferenceClickListener().onPreferenceClick(null); verify(mFeatureFactory.metricsFeatureProvider).logStartedIntent( any(Intent.class), @@ -263,8 +265,8 @@ public class DashboardFeatureProviderImplTest { @Test public void bindPreference_nullPreference_shouldIgnore() { final Tile tile = mock(Tile.class); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, null, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + null /* keys */, tile, "123", Preference.DEFAULT_ORDER); verifyZeroInteractions(tile); } @@ -273,8 +275,8 @@ public class DashboardFeatureProviderImplTest { public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() { final Preference preference = new Preference(RuntimeEnvironment.application); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isNotNull(); @@ -288,9 +290,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isNull(); } @@ -304,8 +305,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -320,8 +321,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString); @@ -336,9 +337,8 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); preference.callChangeListener(false); @@ -358,9 +358,8 @@ public class DashboardFeatureProviderImplTest { final Bundle bundle = new Bundle(); bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true); ShadowTileUtils.setResultBundle(bundle); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); preference.callChangeListener(true); @@ -378,8 +377,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE, mSwitchMetaData); final List observers = mImpl.bindPreferenceToTileAndGetObservers( - mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile, - null /*key */, Preference.DEFAULT_ORDER); + mActivity, mFragment, mForceRoundedIcon, preference, tile, null /* key */, + Preference.DEFAULT_ORDER); ShadowTileUtils.setProviderChecked(false); observers.get(0).onDataChanged(); @@ -397,9 +396,8 @@ public class DashboardFeatureProviderImplTest { final Preference preference = new Preference(RuntimeEnvironment.application); mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, null /* key */, - Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, null /* key */, Preference.DEFAULT_ORDER); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); } @@ -483,8 +481,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", baseOrder); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", baseOrder); assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder); } @@ -496,8 +494,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(testOrder); } @@ -508,8 +506,8 @@ public class DashboardFeatureProviderImplTest { final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello"); - mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon, - MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(mActivity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); } @@ -522,8 +520,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); ShadowActivity shadowActivity = Shadows.shadowOf(activity); @@ -546,8 +544,8 @@ public class DashboardFeatureProviderImplTest { mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); tile.userHandle = null; - mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon, - MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER); + mImpl.bindPreferenceToTileAndGetObservers(activity, mFragment, mForceRoundedIcon, + preference, tile, "123", Preference.DEFAULT_ORDER); preference.performClick(); final ShadowActivity.IntentForResult launchIntent = @@ -669,4 +667,22 @@ public class DashboardFeatureProviderImplTest { assertThat(argument.getValue().getIdentifier()).isEqualTo(0); verify(mActivity, never()).getSupportFragmentManager(); } + + private static class TestFragment extends DashboardFragment { + + @Override + public int getMetricsCategory() { + return MetricsEvent.SETTINGS_GESTURES; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.gestures; + } + + @Override + protected String getLogTag() { + return "TestFragment"; + } + } }