Filter out the duplicate click event on menu page

Whenever the highlighted item is clicked, Settings shouldn't start the
second-layer page again even if the user goes to its child page.
Ex. When the user goes to the Tethering page, clicking on the menu
entry "Network & Internet" should not switch to the page.

Exception: in the deep link case, allow the first click event on the
highlighted item to launch the second-layer page when the page is not
the same as the one on the right pane.

Fix: 215267159
Test: manual, robotest
Change-Id: I2315e0069facc4867cb157752b1a3144716b7d17
This commit is contained in:
Jason Chiu
2022-05-20 22:55:53 +08:00
parent c4899bf019
commit e04dfaa29e
3 changed files with 100 additions and 8 deletions

View File

@@ -180,15 +180,17 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
} }
pref.setOnPreferenceClickListener(preference -> { pref.setOnPreferenceClickListener(preference -> {
TopLevelHighlightMixin highlightMixin = null; TopLevelHighlightMixin highlightMixin = null;
boolean isDuplicateClick = false;
if (fragment instanceof TopLevelSettings if (fragment instanceof TopLevelSettings
&& ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) { && ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) {
// Highlight the preference whenever it's clicked // Highlight the preference whenever it's clicked
final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment; final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment;
topLevelSettings.setHighlightPreferenceKey(key); topLevelSettings.setHighlightPreferenceKey(key);
highlightMixin = topLevelSettings.getHighlightMixin(); highlightMixin = topLevelSettings.getHighlightMixin();
isDuplicateClick = topLevelSettings.isDuplicateClick(preference);
} }
launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory, launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory,
highlightMixin); highlightMixin, isDuplicateClick);
return true; return true;
}); });
} }
@@ -221,7 +223,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
SettingsEnums.DASHBOARD_SUMMARY) SettingsEnums.DASHBOARD_SUMMARY)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY, launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY,
/* highlightMixin= */ null); /* highlightMixin= */ null, /* isDuplicateClick= */ false);
} }
private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) { private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) {
@@ -433,31 +435,45 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
} }
private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent, private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent,
int sourceMetricCategory, TopLevelHighlightMixin highlightMixin) { int sourceMetricCategory, TopLevelHighlightMixin highlightMixin,
boolean isDuplicateClick) {
if (!isIntentResolvable(intent)) { if (!isIntentResolvable(intent)) {
Log.w(TAG, "Cannot resolve intent, skipping. " + intent); Log.w(TAG, "Cannot resolve intent, skipping. " + intent);
return; return;
} }
ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile); ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
if (tile.userHandle == null || tile.isPrimaryProfileOnly()) { if (tile.userHandle == null || tile.isPrimaryProfileOnly()) {
if (!isDuplicateClick) {
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
activity.startActivity(intent); activity.startActivity(intent);
}
} else if (tile.userHandle.size() == 1) { } else if (tile.userHandle.size() == 1) {
if (!isDuplicateClick) {
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
activity.startActivityAsUser(intent, tile.userHandle.get(0)); activity.startActivityAsUser(intent, tile.userHandle.get(0));
}
} else { } else {
final UserHandle userHandle = intent.getParcelableExtra(EXTRA_USER); final UserHandle userHandle = intent.getParcelableExtra(EXTRA_USER);
if (userHandle != null && tile.userHandle.contains(userHandle)) { if (userHandle != null && tile.userHandle.contains(userHandle)) {
if (!isDuplicateClick) {
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
activity.startActivityAsUser(intent, userHandle); activity.startActivityAsUser(intent, userHandle);
}
return; return;
} }
final List<UserHandle> resolvableUsers = getResolvableUsers(intent, tile); final List<UserHandle> resolvableUsers = getResolvableUsers(intent, tile);
if (resolvableUsers.size() == 1) { if (resolvableUsers.size() == 1) {
if (!isDuplicateClick) {
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
activity.startActivityAsUser(intent, resolvableUsers.get(0)); activity.startActivityAsUser(intent, resolvableUsers.get(0));
}
return; return;
} }
// Show the profile select dialog regardless of the duplicate click.
mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory);
ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile, ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile,
sourceMetricCategory, /* onShowListener= */ highlightMixin, sourceMetricCategory, /* onShowListener= */ highlightMixin,
/* onDismissListener= */ highlightMixin, /* onDismissListener= */ highlightMixin,

View File

@@ -140,6 +140,10 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
} }
} }
String getHighlightPreferenceKey() {
return mCurrentKey;
}
void highlightPreferenceIfNeeded() { void highlightPreferenceIfNeeded() {
if (mTopLevelAdapter != null) { if (mTopLevelAdapter != null) {
mTopLevelAdapter.requestHighlight(); mTopLevelAdapter.requestHighlight();

View File

@@ -16,12 +16,17 @@
package com.android.settings.homepage; package com.android.settings.homepage;
import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI;
import static com.android.settings.search.actionbar.SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR; import static com.android.settings.search.actionbar.SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR;
import static com.android.settingslib.search.SearchIndexable.MOBILE; import static com.android.settingslib.search.SearchIndexable.MOBILE;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
@@ -38,6 +43,7 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.window.embedding.SplitController; import androidx.window.embedding.SplitController;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.activityembedding.ActivityEmbeddingUtils;
@@ -51,12 +57,15 @@ import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import java.net.URISyntaxException;
@SearchIndexable(forTarget = MOBILE) @SearchIndexable(forTarget = MOBILE)
public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener, public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener,
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private static final String TAG = "TopLevelSettings"; private static final String TAG = "TopLevelSettings";
private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin"; private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin";
private static final String SAVED_FIRST_PREFERENCE_CLICK = "first_pref_click";
private static final String PREF_KEY_SUPPORT = "top_level_support"; private static final String PREF_KEY_SUPPORT = "top_level_support";
private boolean mIsEmbeddingActivityEnabled; private boolean mIsEmbeddingActivityEnabled;
@@ -64,6 +73,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
private int mPaddingHorizontal; private int mPaddingHorizontal;
private boolean mScrollNeeded = true; private boolean mScrollNeeded = true;
private boolean mFirstStarted = true; private boolean mFirstStarted = true;
private boolean mFirstDuplicateClickCheck = true;
public TopLevelSettings() { public TopLevelSettings() {
final Bundle args = new Bundle(); final Bundle args = new Bundle();
@@ -107,6 +117,10 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
@Override @Override
public boolean onPreferenceTreeClick(Preference preference) { public boolean onPreferenceTreeClick(Preference preference) {
if (isDuplicateClick(preference)) {
return true;
}
// Register SplitPairRule for SubSettings. // Register SplitPairRule for SubSettings.
ActivityEmbeddingRulesController.registerSubSettingsPairRule(getContext(), ActivityEmbeddingRulesController.registerSubSettingsPairRule(getContext(),
true /* clearTop */); true /* clearTop */);
@@ -140,6 +154,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
boolean activityEmbedded = SplitController.getInstance().isActivityEmbedded(getActivity()); boolean activityEmbedded = SplitController.getInstance().isActivityEmbedded(getActivity());
if (icicle != null) { if (icicle != null) {
mFirstDuplicateClickCheck = icicle.getBoolean(SAVED_FIRST_PREFERENCE_CLICK);
mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN); mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN);
mScrollNeeded = !mHighlightMixin.isActivityEmbedded() && activityEmbedded; mScrollNeeded = !mHighlightMixin.isActivityEmbedded() && activityEmbedded;
mHighlightMixin.setActivityEmbedded(activityEmbedded); mHighlightMixin.setActivityEmbedded(activityEmbedded);
@@ -173,6 +188,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_FIRST_PREFERENCE_CLICK, mFirstDuplicateClickCheck);
if (mHighlightMixin != null) { if (mHighlightMixin != null) {
outState.putParcelable(SAVED_HIGHLIGHT_MIXIN, mHighlightMixin); outState.putParcelable(SAVED_HIGHLIGHT_MIXIN, mHighlightMixin);
} }
@@ -271,6 +287,41 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
} }
} }
/** Returns whether clicking the specified preference is considered as a duplicate click. */
public boolean isDuplicateClick(Preference pref) {
boolean firstCheck = mFirstDuplicateClickCheck;
mFirstDuplicateClickCheck = false;
/*
* Return false when
* 1. The device doesn't support activity embedding
* 2. The target preference is not highlighted
* 3. The current activity is not embedded
*/
if (mHighlightMixin == null
|| !TextUtils.equals(pref.getKey(), mHighlightMixin.getHighlightPreferenceKey())
|| !SplitController.getInstance().isActivityEmbedded(getActivity())) {
return false;
}
/*
* Return true when
* 1. This method has been called before
* 2. The preference doesn't have a target fragment, ex. Wallpaper and injections
*/
if (!firstCheck || TextUtils.isEmpty(pref.getFragment())) {
return true;
}
/*
* Returns true when
* 1. The right pane fragment is not started by a deep link.
* 2. The target fragment equals the right pane fragment
*/
String intentUri = getIntent().getStringExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI);
return TextUtils.isEmpty(intentUri)
|| TextUtils.equals(pref.getFragment(), getIntentTargetFragment(intentUri));
}
/** Show/hide the highlight on the menu entry for the search page presence */ /** Show/hide the highlight on the menu entry for the search page presence */
public void setMenuHighlightShowed(boolean show) { public void setMenuHighlightShowed(boolean show) {
if (mHighlightMixin != null) { if (mHighlightMixin != null) {
@@ -310,6 +361,27 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi
} }
} }
private String getIntentTargetFragment(String intentUri) {
Intent targetIntent;
try {
targetIntent = Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException e) {
return null;
}
String fragment = targetIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT);
if (!TextUtils.isEmpty(fragment)) {
return fragment;
}
ActivityInfo info = targetIntent.resolveActivityInfo(getPackageManager(),
PackageManager.GET_META_DATA);
if (info == null || info.metaData == null) {
return null;
}
return info.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
}
private void iteratePreferences(PreferenceJob job) { private void iteratePreferences(PreferenceJob job) {
if (job == null || getPreferenceManager() == null) { if (job == null || getPreferenceManager() == null) {
return; return;