Revise homepage highlight mechanism
- Create TopLevelHighlightMixin to handle highlight actions and simplify
TopLevelSettings
- Fix the error highlight and the flicker after screen rotation
- Postpone creating the fragment until it's needed to accelerate the
initialization and to fix the search highlight function breakage after
toggling light/dark mode
- Register activity embedding rules only once for injection and
wallpaper
- Do not highlight Tips & support since it's full screen
- Refactor ActivityEmbeddingRulesController
Bug: 207316936
Test: manual, robotest build pass
Change-Id: If322ec180b03ee123987c70779a25c6a570d9faf
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index fa61994..8484324 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -23,7 +23,6 @@
import android.util.LayoutDirection;
import android.util.Log;
-import androidx.annotation.NonNull;
import androidx.window.embedding.ActivityFilter;
import androidx.window.embedding.ActivityRule;
import androidx.window.embedding.SplitController;
@@ -114,7 +113,7 @@
registerTwoPanePairRule(
context,
- getComponentName(context, Settings.class),
+ new ComponentName(context, Settings.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
@@ -123,7 +122,7 @@
registerTwoPanePairRule(
context,
- getComponentName(context, SettingsHomepageActivity.class),
+ new ComponentName(context, SettingsHomepageActivity.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
@@ -143,7 +142,7 @@
registerTwoPanePairRule(
context,
- getComponentName(context, SliceDeepLinkHomepageActivity.class),
+ new ComponentName(context, SliceDeepLinkHomepageActivity.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ALWAYS : SplitRule.FINISH_NEVER,
@@ -179,7 +178,7 @@
registerTwoPanePairRuleForSettingsHome(
context,
- getComponentName(context, SubSettings.class),
+ new ComponentName(context, SubSettings.class),
null /* secondaryIntentAction */,
clearTop);
}
@@ -191,8 +190,7 @@
addActivityFilter(activityFilters, SliceDeepLinkHomepageActivity.class);
addActivityFilter(activityFilters, Settings.class);
- final Intent intent = new Intent();
- intent.setComponent(getComponentName(Settings.NetworkDashboardActivity.class));
+ final Intent intent = new Intent(mContext, Settings.NetworkDashboardActivity.class);
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule(
activityFilters,
intent,
@@ -215,23 +213,7 @@
private void addActivityFilter(Set<ActivityFilter> activityFilters,
Class<? extends Activity> activityClass) {
- activityFilters.add(new ActivityFilter(getComponentName(activityClass),
+ activityFilters.add(new ActivityFilter(new ComponentName(mContext, activityClass),
null /* intentAction */));
}
-
- private void addActivityFilter(Set<ActivityFilter> activityFilters,
- ComponentName componentName) {
- activityFilters.add(new ActivityFilter(componentName, null /* intentAction */));
- }
-
- @NonNull
- private ComponentName getComponentName(Class<? extends Activity> activityClass) {
- return getComponentName(mContext, activityClass);
- }
-
- @NonNull
- private static ComponentName getComponentName(Context context,
- Class<? extends Activity> activityClass) {
- return new ComponentName(context.getPackageName(), activityClass.getName());
- }
}
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 3d1381d..69fdc9e 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -36,7 +36,6 @@
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;
@@ -63,6 +62,7 @@
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
+import com.android.settings.homepage.TopLevelHighlightMixin;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.PrimarySwitchPreference;
@@ -170,27 +170,23 @@
if (action != null) {
intent.setAction(action);
}
+ // Register the rule for injected apps.
+ ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome(
+ mContext,
+ new ComponentName(tile.getPackageName(), tile.getComponentName()),
+ action,
+ true /* clearTop */);
pref.setOnPreferenceClickListener(preference -> {
- OnCancelListener listener = null;
+ TopLevelHighlightMixin highlightMixin = null;
if (fragment instanceof TopLevelSettings
&& ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) {
- // Register the rule for injected apps.
- ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome(
- mContext,
- new ComponentName(tile.getPackageName(), tile.getComponentName()),
- null /* secondaryIntentAction */,
- true /* clearTop */);
-
- // Highlight preference ui.
+ // Highlight the preference whenever it's clicked
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();
+ highlightMixin = topLevelSettings.getHighlightMixin();
}
launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory,
- listener);
+ highlightMixin);
return true;
});
}
@@ -223,7 +219,7 @@
SettingsEnums.DASHBOARD_SUMMARY)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY,
- /* listener= */ null);
+ /* highlightMixin= */ null);
}
private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) {
@@ -438,7 +434,7 @@
}
private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent,
- int sourceMetricCategory, OnCancelListener listener) {
+ int sourceMetricCategory, TopLevelHighlightMixin highlightMixin) {
if (!isIntentResolvable(intent)) {
Log.w(TAG, "Cannot resolve intent, skipping. " + intent);
return;
@@ -469,7 +465,9 @@
}
ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile,
- sourceMetricCategory, listener);
+ sourceMetricCategory, /* onShowListener= */ highlightMixin,
+ /* onDismissListener= */ highlightMixin,
+ /* onCancelListener= */ highlightMixin);
}
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index b05f23b..e0ba378 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -21,6 +21,8 @@
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.DialogInterface.OnShowListener;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -45,23 +47,30 @@
private int mSourceMetricCategory;
private Tile mSelectedTile;
+ private OnShowListener mOnShowListener;
private OnCancelListener mOnCancelListener;
+ private OnDismissListener mOnDismissListener;
/**
* 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.
+ * @param onShowListener The listener listens to the dialog showing event.
+ * @param onDismissListener The listener listens to the dialog dismissing event.
+ * @param onCancelListener The listener listens to the dialog cancelling event.
*/
public static void show(FragmentManager manager, Tile tile, int sourceMetricCategory,
- OnCancelListener listener) {
+ OnShowListener onShowListener, OnDismissListener onDismissListener,
+ OnCancelListener onCancelListener) {
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.mOnShowListener = onShowListener;
+ dialog.mOnDismissListener = onDismissListener;
+ dialog.mOnCancelListener = onCancelListener;
dialog.show(manager, "select_profile");
}
@@ -97,12 +106,30 @@
}
@Override
+ public void onStart() {
+ super.onStart();
+ // The fragment shows the dialog within onStart()
+ if (mOnShowListener != null) {
+ mOnShowListener.onShow(getDialog());
+ }
+ }
+
+ @Override
public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
if (mOnCancelListener != null) {
mOnCancelListener.onCancel(dialog);
}
}
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss(dialog);
+ }
+ }
+
public static void updateUserHandlesIfNeeded(Context context, Tile tile) {
final List<UserHandle> userHandles = tile.userHandle;
if (tile.userHandle == null || tile.userHandle.size() <= 1) {
diff --git a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
index 7640d08..0136eac 100644
--- a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
+++ b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
@@ -63,6 +63,11 @@
super.displayPreference(screen);
Preference preference = screen.findPreference(getPreferenceKey());
preference.setTitle(getTitle());
+ ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome(
+ mContext,
+ getComponentName(),
+ null /* secondaryIntentAction */,
+ true /* clearTop */);
}
public String getTitle() {
@@ -103,11 +108,6 @@
mContext)) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
- ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome(
- mContext,
- intent.getComponent(),
- null /* secondaryIntentAction */,
- true /* clearTop */);
preference.getContext().startActivity(intent);
return true;
}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 31cc7aa..e25e1f5 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -76,7 +76,7 @@
public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA =
"settings_large_screen_deep_link_intent_data";
- private static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network;
+ static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network;
private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300;
private TopLevelSettings mMainFragment;
@@ -94,6 +94,10 @@
void onHomepageLoaded();
}
+ private interface FragmentBuilder<T extends Fragment> {
+ T build();
+ }
+
/**
* Try to add a {@link HomepageLoadedListener}. If homepage is already loaded, the listener
* will not be notified.
@@ -168,13 +172,15 @@
initAvatarView();
showSuggestionFragment();
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
- showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
+ showFragment(() -> new ContextualCardsFragment(), R.id.contextual_cards_content);
}
}
- mMainFragment = new TopLevelSettings();
- mMainFragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
- getHighlightMenuKey());
- showFragment(mMainFragment, R.id.main_content);
+ mMainFragment = showFragment(() -> {
+ final TopLevelSettings fragment = new TopLevelSettings();
+ fragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
+ getHighlightMenuKey());
+ return fragment;
+ }, R.id.main_content);
((FrameLayout) findViewById(R.id.main_content))
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
@@ -260,9 +266,9 @@
}
private void showSuggestionFragment() {
- final Class<? extends Fragment> fragment = FeatureFactory.getFactory(this)
+ final Class<? extends Fragment> fragmentClass = FeatureFactory.getFactory(this)
.getSuggestionFeatureProvider(this).getContextualSuggestionFragment();
- if (fragment == null) {
+ if (fragmentClass == null) {
return;
}
@@ -274,28 +280,33 @@
// Schedule a timer to show the homepage and hide the suggestion on timeout.
mHomepageView.postDelayed(() -> showHomepageWithSuggestion(false),
HOMEPAGE_LOADING_TIMEOUT_MS);
- try {
- showFragment(fragment.getConstructor().newInstance(), R.id.suggestion_content);
- if (mIsEmbeddingActivityEnabled) {
- showFragment(fragment.getConstructor().newInstance(),
- R.id.two_pane_suggestion_content);
+ final FragmentBuilder<?> fragmentBuilder = () -> {
+ try {
+ return fragmentClass.getConstructor().newInstance();
+ } catch (Exception e) {
+ Log.w(TAG, "Cannot show fragment", e);
}
- } catch (Exception e) {
- Log.w(TAG, "Cannot show fragment", e);
+ return null;
+ };
+ showFragment(fragmentBuilder, R.id.suggestion_content);
+ if (mIsEmbeddingActivityEnabled) {
+ showFragment(fragmentBuilder, R.id.two_pane_suggestion_content);
}
}
- private void showFragment(Fragment fragment, int id) {
+ private <T extends Fragment> T showFragment(FragmentBuilder<T> fragmentBuilder, int id) {
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
- final Fragment showFragment = fragmentManager.findFragmentById(id);
+ T showFragment = (T) fragmentManager.findFragmentById(id);
if (showFragment == null) {
- fragmentTransaction.add(id, fragment);
+ showFragment = fragmentBuilder.build();
+ fragmentTransaction.add(id, showFragment);
} else {
fragmentTransaction.show(showFragment);
}
fragmentTransaction.commit();
+ return showFragment;
}
private void launchDeepLinkIntentToRight() {
@@ -363,14 +374,14 @@
targetIntent.getAction(),
SplitRule.FINISH_ALWAYS,
SplitRule.FINISH_ALWAYS,
- true /* clearTop*/);
+ true /* clearTop */);
ActivityEmbeddingRulesController.registerTwoPanePairRule(this,
- new ComponentName(Settings.class.getPackageName(), Settings.class.getName()),
+ new ComponentName(getApplicationContext(), Settings.class),
targetComponentName,
targetIntent.getAction(),
SplitRule.FINISH_ALWAYS,
SplitRule.FINISH_ALWAYS,
- true /* clearTop*/);
+ true /* clearTop */);
startActivity(targetIntent);
}
diff --git a/src/com/android/settings/homepage/TopLevelHighlightMixin.java b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
new file mode 100644
index 0000000..ebfe7f2
--- /dev/null
+++ b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
@@ -0,0 +1,189 @@
+/*
+ * 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 android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.widget.HighlightableTopLevelPreferenceAdapter;
+
+/** A highlight mixin for the top level settings fragment. */
+public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnShowListener,
+ DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
+
+ private static final String TAG = "TopLevelHighlightMixin";
+
+ private String mCurrentKey;
+ // Stores the previous key for the profile select dialog cancel event
+ private String mPreviousKey;
+ // Stores the key hidden for the search page presence
+ private String mHiddenKey;
+ private DialogInterface mDialog;
+ private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter;
+
+ public TopLevelHighlightMixin() {
+ }
+
+ public TopLevelHighlightMixin(Parcel source) {
+ mCurrentKey = source.readString();
+ mPreviousKey = source.readString();
+ mHiddenKey = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mCurrentKey);
+ dest.writeString(mPreviousKey);
+ dest.writeString(mHiddenKey);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TopLevelHighlightMixin> CREATOR = new Creator<>() {
+ @Override
+ public TopLevelHighlightMixin createFromParcel(Parcel source) {
+ return new TopLevelHighlightMixin(source);
+ }
+
+ @Override
+ public TopLevelHighlightMixin[] newArray(int size) {
+ return new TopLevelHighlightMixin[size];
+ }
+ };
+
+ @Override
+ public void onShow(DialogInterface dialog) {
+ mDialog = dialog;
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mDialog = null;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (mTopLevelAdapter != null) {
+ mCurrentKey = mPreviousKey;
+ mPreviousKey = null;
+ mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ false);
+ }
+ }
+
+ RecyclerView.Adapter onCreateAdapter(TopLevelSettings topLevelSettings,
+ PreferenceScreen preferenceScreen) {
+ if (TextUtils.isEmpty(mCurrentKey)) {
+ mCurrentKey = getHighlightPrefKeyFromArguments(topLevelSettings.getArguments());
+ }
+
+ Log.d(TAG, "onCreateAdapter, pref key: " + mCurrentKey);
+ mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(
+ (SettingsHomepageActivity) topLevelSettings.getActivity(), preferenceScreen,
+ topLevelSettings.getListView(), mCurrentKey);
+ return mTopLevelAdapter;
+ }
+
+ void reloadHighlightMenuKey(Bundle arguments) {
+ if (mTopLevelAdapter == null) {
+ return;
+ }
+ ensureDialogDismissed();
+
+ mCurrentKey = getHighlightPrefKeyFromArguments(arguments);
+ Log.d(TAG, "reloadHighlightMenuKey, pref key: " + mCurrentKey);
+ mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ true);
+ }
+
+ void setHighlightPreferenceKey(String prefKey) {
+ if (mTopLevelAdapter != null) {
+ ensureDialogDismissed();
+ mPreviousKey = mCurrentKey;
+ mCurrentKey = prefKey;
+ mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false);
+ }
+ }
+
+ void highlightPreferenceIfNeeded(FragmentActivity activity) {
+ if (mTopLevelAdapter != null) {
+ mTopLevelAdapter.requestHighlight();
+ }
+ }
+
+ void setMenuHighlightShowed(boolean show) {
+ if (mTopLevelAdapter == null) {
+ return;
+ }
+ ensureDialogDismissed();
+
+ if (show) {
+ mCurrentKey = mHiddenKey;
+ mHiddenKey = null;
+ } else {
+ if (mHiddenKey == null) {
+ mHiddenKey = mCurrentKey;
+ }
+ mCurrentKey = null;
+ }
+ mTopLevelAdapter.highlightPreference(mCurrentKey, /* scrollNeeded= */ show);
+ }
+
+ void setHighlightMenuKey(String menuKey, boolean scrollNeeded) {
+ if (mTopLevelAdapter == null) {
+ return;
+ }
+ ensureDialogDismissed();
+
+ 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);
+ mCurrentKey = prefKey;
+ mTopLevelAdapter.highlightPreference(prefKey, scrollNeeded);
+ }
+ }
+
+ private static String getHighlightPrefKeyFromArguments(Bundle arguments) {
+ 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;
+ }
+
+ private void ensureDialogDismissed() {
+ if (mDialog != null) {
+ onCancel(mDialog);
+ mDialog.dismiss();
+ }
+ }
+}
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index eb1a066..2bb8e34 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -25,7 +25,6 @@
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;
@@ -34,7 +33,6 @@
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.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
@@ -42,7 +40,6 @@
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.settings.widget.HomepagePreference;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.drawer.Tile;
@@ -53,13 +50,10 @@
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private static final String TAG = "TopLevelSettings";
- private static final String SAVED_HIGHLIGHTED_PREF = "highlighted_pref";
- private static final String SAVED_CACHED_PREF = "cached_pref";
+ private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin";
+ private static final String PREF_KEY_SUPPORT = "top_level_support";
- private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter;
-
- private String mHighlightedPreferenceKey;
- private String mCachedPreferenceKey;
+ private TopLevelHighlightMixin mHighlightMixin;
public TopLevelSettings() {
final Bundle args = new Bundle();
@@ -127,17 +121,35 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- if (icicle != null) {
- mHighlightedPreferenceKey = icicle.getString(SAVED_HIGHLIGHTED_PREF);
- mCachedPreferenceKey = icicle.getString(SAVED_CACHED_PREF);
+ if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) {
+ return;
}
+
+ if (icicle != null) {
+ mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN);
+ }
+ if (mHighlightMixin == null) {
+ mHighlightMixin = new TopLevelHighlightMixin();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ // Set default highlight menu key for 1-pane homepage since it will show the placeholder
+ // page once changing back to 2-pane.
+ if (!ActivityEmbeddingUtils.isTwoPaneResolution(getActivity())) {
+ setHighlightMenuKey(getString(SettingsHomepageActivity.DEFAULT_HIGHLIGHT_MENU_KEY),
+ /* scrollNeeded= */ false);
+ }
+ super.onStart();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putString(SAVED_HIGHLIGHTED_PREF, mHighlightedPreferenceKey);
- outState.putString(SAVED_CACHED_PREF, mCachedPreferenceKey);
+ if (mHighlightMixin != null) {
+ outState.putParcelable(SAVED_HIGHLIGHT_MIXIN, mHighlightMixin);
+ }
}
@Override
@@ -170,58 +182,35 @@
@Override
public void highlightPreferenceIfNeeded() {
- if (mTopLevelAdapter != null) {
- mTopLevelAdapter.requestHighlight();
+ if (mHighlightMixin != null) {
+ mHighlightMixin.highlightPreferenceIfNeeded(getActivity());
}
}
+ /** Returns a {@link TopLevelHighlightMixin} that performs highlighting */
+ public TopLevelHighlightMixin getHighlightMixin() {
+ return mHighlightMixin;
+ }
+
/** Highlight a preference with specified preference key */
public void setHighlightPreferenceKey(String prefKey) {
- if (mTopLevelAdapter != null) {
- mCachedPreferenceKey = null;
- mHighlightedPreferenceKey = prefKey;
- mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ false);
+ // Skip Tips & support since it's full screen
+ if (mHighlightMixin != null && !TextUtils.equals(prefKey, PREF_KEY_SUPPORT)) {
+ mHighlightMixin.setHighlightPreferenceKey(prefKey);
}
}
- /** Highlight the previous preference */
- public void restorePreviousHighlight() {
- if (mTopLevelAdapter != null) {
- mTopLevelAdapter.restorePreviousHighlight();
- }
- }
-
- /** Show/hide the highlight on the menu entry */
+ /** Show/hide the highlight on the menu entry for the search page presence */
public void setMenuHighlightShowed(boolean show) {
- if (mTopLevelAdapter == null) {
- return;
+ if (mHighlightMixin != null) {
+ mHighlightMixin.setMenuHighlightShowed(show);
}
-
- if (show) {
- mHighlightedPreferenceKey = mCachedPreferenceKey;
- mCachedPreferenceKey = null;
- } else {
- if (mCachedPreferenceKey == null) {
- mCachedPreferenceKey = mHighlightedPreferenceKey;
- }
- mHighlightedPreferenceKey = null;
- }
- mTopLevelAdapter.highlightPreference(mHighlightedPreferenceKey, /* scrollNeeded= */ show);
}
/** Highlight and scroll to a preference with specified menu key */
- public void setHighlightMenuKey(String menuKey) {
- if (mTopLevelAdapter == null) {
- return;
- }
-
- 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);
- mHighlightedPreferenceKey = prefKey;
- mTopLevelAdapter.highlightPreference(prefKey, /* scrollNeeded= */ true);
+ public void setHighlightMenuKey(String menuKey, boolean scrollNeeded) {
+ if (mHighlightMixin != null) {
+ mHighlightMixin.setHighlightMenuKey(menuKey, scrollNeeded);
}
}
@@ -237,16 +226,7 @@
|| !(getActivity() instanceof SettingsHomepageActivity)) {
return super.onCreateAdapter(preferenceScreen);
}
-
- if (TextUtils.isEmpty(mHighlightedPreferenceKey)) {
- mHighlightedPreferenceKey = getHighlightPrefKeyFromArguments();
- }
-
- Log.d(TAG, "onCreateAdapter, pref key: " + mHighlightedPreferenceKey);
- mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(
- (SettingsHomepageActivity) getActivity(), preferenceScreen, getListView(),
- mHighlightedPreferenceKey);
- return mTopLevelAdapter;
+ return mHighlightMixin.onCreateAdapter(this, preferenceScreen);
}
@Override
@@ -255,25 +235,9 @@
}
void reloadHighlightMenuKey() {
- if (mTopLevelAdapter == null) {
- return;
+ if (mHighlightMixin != null) {
+ mHighlightMixin.reloadHighlightMenuKey(getArguments());
}
-
- 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 =
diff --git a/src/com/android/settings/search/SearchResultTrampoline.java b/src/com/android/settings/search/SearchResultTrampoline.java
index f9cbc36..8b041b6 100644
--- a/src/com/android/settings/search/SearchResultTrampoline.java
+++ b/src/com/android/settings/search/SearchResultTrampoline.java
@@ -109,7 +109,8 @@
final SettingsHomepageActivity homeActivity =
((SettingsApplication) getApplicationContext()).getHomeActivity();
if (homeActivity != null) {
- homeActivity.getMainFragment().setHighlightMenuKey(highlightMenuKey);
+ homeActivity.getMainFragment().setHighlightMenuKey(highlightMenuKey,
+ /* scrollNeeded= */ true);
}
} else {
// Two-pane case
diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
index b7f3015..ff8f805 100644
--- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
+++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
@@ -59,17 +60,18 @@
private final int mNormalBackgroundRes;
private final int mHighlightBackgroundRes;
private String mHighlightKey;
- private String mPreviousHighlightKey;
private int mHighlightPosition = RecyclerView.NO_POSITION;
private int mScrollPosition = RecyclerView.NO_POSITION;
private boolean mHighlightNeeded;
private boolean mScrolled;
+ private SparseArray<PreferenceViewHolder> mViewHolders;
public HighlightableTopLevelPreferenceAdapter(SettingsHomepageActivity homepageActivity,
PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key) {
super(preferenceGroup);
mRecyclerView = recyclerView;
mHighlightKey = key;
+ mViewHolders = new SparseArray<>();
mContext = preferenceGroup.getContext();
mHomepageActivity = homepageActivity;
final TypedValue outValue = new TypedValue();
@@ -92,6 +94,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
+ mViewHolders.put(position, holder);
updateBackground(holder, position);
}
@@ -120,9 +123,9 @@
return;
}
+ final int previousPosition = mHighlightPosition;
if (TextUtils.isEmpty(mHighlightKey)) {
// De-highlight previous preference.
- final int previousPosition = mHighlightPosition;
mHighlightPosition = RecyclerView.NO_POSITION;
mScrolled = true;
if (previousPosition >= 0) {
@@ -145,10 +148,14 @@
// Turn on/off highlight when screen split mode is changed.
if (highlightNeeded != mHighlightNeeded) {
- Log.d(TAG, "Highlight change needed: " + highlightNeeded);
+ Log.d(TAG, "Highlight needed change: " + highlightNeeded);
mHighlightNeeded = highlightNeeded;
mHighlightPosition = position;
notifyItemChanged(position);
+ if (!highlightNeeded) {
+ // De-highlight to prevent a flicker
+ removeHighlightAt(previousPosition);
+ }
return;
}
@@ -156,7 +163,6 @@
return;
}
- final int previousPosition = mHighlightPosition;
mHighlightPosition = position;
Log.d(TAG, "Request highlight position " + position);
Log.d(TAG, "Is highlight needed: " + highlightNeeded);
@@ -178,20 +184,11 @@
* 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();
- }
-
@Override
public void onHomepageLoaded() {
scroll();
@@ -224,6 +221,17 @@
}
}
+ private void removeHighlightAt(int position) {
+ if (position >= 0) {
+ // De-highlight the existing preference view holder at an early stage
+ final PreferenceViewHolder holder = mViewHolders.get(position);
+ if (holder != null) {
+ removeHighlightBackground(holder);
+ }
+ notifyItemChanged(position);
+ }
+ }
+
private void addHighlightBackground(PreferenceViewHolder holder) {
final View v = holder.itemView;
v.setBackgroundResource(mHighlightBackgroundRes);