2 panes deep link for large screen devices
This change supports deep link to Settings app internal pages
and external pages outside Settings app.
Apps need android.permission.ALLOW_TWO_PANES_DEEP_LINK_IN_SETTINGS
to send the intent of Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK.
Settings app will startActivity for the intent from
Settings#EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI.
Bug: 197048599
Test: build pass
Change-Id: Idaf4a8be4603c1308f16fb4e378266c1e52acb40
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index f3cdd6c..178892e 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -58,6 +58,8 @@
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.gateway.SettingsGateway;
import com.android.settings.dashboard.DashboardFeatureProvider;
+import com.android.settings.activityembedding.ActivityEmbeddingUtils;
+import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wfd.WifiDisplaySettings;
@@ -240,7 +242,22 @@
// Should happen before any call to getIntent()
getMetaData();
+ // If it's a deep link intent, start the Activity from SettingsHomepageActivity for 2-pane.
final Intent intent = getIntent();
+ final boolean isFromSettingsHomepage = intent.getBooleanExtra(
+ SettingsHomepageActivity.EXTRA_IS_FROM_SETTINGS_HOMEPAGE, /* defaultValue */ false);
+ if (ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this) && !isFromSettingsHomepage
+ && isOnlyOneActivityInActivityStack()) {
+ final Intent trampolineIntent =
+ new Intent(android.provider.Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK);
+ trampolineIntent.putExtra(
+ android.provider.Settings.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI,
+ intent.toUri(Intent.URI_INTENT_SCHEME));
+ startActivity(trampolineIntent);
+ finish();
+ return;
+ }
+
if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
}
@@ -347,6 +364,12 @@
}
}
+ private boolean isOnlyOneActivityInActivityStack() {
+ final ActivityManager activityManager = getSystemService(ActivityManager.class);
+ List<ActivityManager.RunningTaskInfo> taskList = activityManager.getRunningTasks(2);
+ return taskList.get(0).numActivities == 1;
+ }
+
/** Returns the initial fragment name that the activity will launch. */
@VisibleForTesting
public String getInitialFragmentName(Intent intent) {
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index f1a1ecd..9940980 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -60,12 +60,34 @@
mSplitController.clearRegisteredRules();
// Set a placeholder for home page.
- mSplitController.registerRule(getHomepagePlaceholderRule());
+ registerHomepagePlaceholderRule();
// Set subsettings rule.
- mSplitController.registerRule(getSubSettingsPairRule());
+ registerTwoPanePairRule(mContext,
+ getComponentName(Settings.class),
+ getComponentName(SubSettings.class),
+ true /* finishPrimaryWithSecondary */,
+ true /* finishSecondaryWithPrimary */);
}
- private SplitPlaceholderRule getHomepagePlaceholderRule() {
+ /** Register a SplitPairRule for 2-pane. */
+ public static void registerTwoPanePairRule(Context context,
+ ComponentName primary, ComponentName secondary,
+ boolean finishPrimaryWithSecondary, boolean finishSecondaryWithPrimary) {
+ final Set<SplitPairFilter> filters = new HashSet<>();
+ filters.add(new SplitPairFilter(primary, secondary,
+ null /* secondaryActivityIntentAction */,
+ null /* secondaryActivityIntentCategory */));
+
+ new SplitController(context).registerRule(new SplitPairRule(filters,
+ finishPrimaryWithSecondary,
+ finishSecondaryWithPrimary, true /* clearTop */,
+ ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(context),
+ ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(context),
+ ActivityEmbeddingUtils.SPLIT_RATIO,
+ LayoutDirection.LOCALE));
+ }
+
+ private void registerHomepagePlaceholderRule() {
final Set<ActivityFilter> activityFilters = new HashSet<>();
activityFilters.add(new ActivityFilter(getComponentName(Settings.class)));
final Intent intent = new Intent();
@@ -78,27 +100,7 @@
ActivityEmbeddingUtils.SPLIT_RATIO,
LayoutDirection.LOCALE);
- return placeholderRule;
- }
-
- private SplitPairRule getSubSettingsPairRule() {
- final Set<SplitPairFilter> pairFilters = new HashSet<>();
- pairFilters.add(new SplitPairFilter(
- getComponentName(Settings.class),
- getComponentName(SubSettings.class),
- null /* secondaryActivityIntentAction */,
- null /* secondaryActivityIntentCategory */));
- final SplitPairRule rule = new SplitPairRule(
- pairFilters,
- true /* finishPrimaryWithSecondary */,
- true /* finishSecondaryWithPrimary */,
- true /* clearTop */,
- ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(mContext),
- ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(mContext),
- ActivityEmbeddingUtils.SPLIT_RATIO,
- LayoutDirection.LOCALE);
-
- return rule;
+ mSplitController.registerRule(placeholderRule);
}
@NonNull
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 1d7b5dc..73f0abb 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -18,8 +18,14 @@
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.provider.Settings;
+import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
@@ -31,21 +37,33 @@
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.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.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
+import java.net.URISyntaxException;
+
/** Settings homepage activity */
public class SettingsHomepageActivity extends FragmentActivity implements
CategoryMixin.CategoryHandler {
private static final String TAG = "SettingsHomepageActivity";
+ // Put true value to the intent when startActivity for a deep link intent from this Activity.
+ public static final String EXTRA_IS_FROM_SETTINGS_HOMEPAGE = "is_from_settings_homepage";
+
+ // An alias class name of SettingsHomepageActivity.
+ private static final String ALIAS_DEEP_LINK = "com.android.settings.DeepLinkHomepageActivity";
+
private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300;
private View mHomepageView;
@@ -105,6 +123,20 @@
showFragment(new TopLevelSettings(), R.id.main_content);
((FrameLayout) findViewById(R.id.main_content))
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
+
+ // Launch the intent from deep link for large screen devices.
+ launchDeepLinkIntentToRight();
+ }
+
+ @Override
+ 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.
+ setIntent(intent);
+ // Launch the intent from deep link for large screen devices.
+ launchDeepLinkIntentToRight();
}
private void showSuggestionFragment() {
@@ -141,6 +173,54 @@
fragmentTransaction.commit();
}
+ private void launchDeepLinkIntentToRight() {
+ if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
+ return;
+ }
+
+ final Intent intent = getIntent();
+ if (intent == null || !TextUtils.equals(intent.getAction(),
+ Settings.ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK)) {
+ return;
+ }
+
+ final String intentUriString = intent.getStringExtra(
+ Settings.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();
+ return;
+ }
+
+ final Intent targetIntent;
+ try {
+ targetIntent = Intent.parseUri(intentUriString, Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Failed to parse deep link intent: " + e);
+ finish();
+ return;
+ }
+
+ final ComponentName targetComponentName = targetIntent.resolveActivity(getPackageManager());
+ if (targetComponentName == null) {
+ Log.e(TAG, "No valid target for the deep link intent: " + targetIntent);
+ finish();
+ return;
+ }
+
+ targetIntent.setFlags(targetIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ targetIntent.putExtra(EXTRA_IS_FROM_SETTINGS_HOMEPAGE, true);
+
+ // Set 2-pane pair rule for the external deep link page.
+ ActivityEmbeddingRulesController.registerTwoPanePairRule(this,
+ new ComponentName(Utils.SETTINGS_PACKAGE_NAME, ALIAS_DEEP_LINK),
+ targetComponentName,
+ true /* finishPrimaryWithSecondary */,
+ true /* finishSecondaryWithPrimary */);
+ startActivity(targetIntent);
+ }
+
private void initHomepageContainer() {
final View view = findViewById(R.id.homepage_container);
// Prevent inner RecyclerView gets focus and invokes scrolling.