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/AndroidManifest.xml b/AndroidManifest.xml
index d8bbe8f..2c3eb05 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -107,6 +107,8 @@
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
     <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
     <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />
+    <uses-permission android:name="android.permission.LAUNCH_TWO_PANE_SETTINGS_DEEP_LINK" />
+    <uses-permission android:name="android.permission.ALLOW_PLACE_IN_TWO_PANE_SETTINGS" />
 
     <application
             android:name=".SettingsApplication"
@@ -172,6 +174,17 @@
             <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
         </activity-alias>
 
+        <!-- Alias for SettingsHomepageActivity which works for deep link page in 2-panel. -->
+        <activity-alias android:name="DeepLinkHomepageActivity"
+                android:label="@string/settings_label_launcher"
+                android:exported="true"
+                android:targetActivity=".homepage.SettingsHomepageActivity">
+            <intent-filter>
+                <action android:name="android.settings.SETTINGS_LARGE_SCREEN_DEEP_LINK" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+
         <receiver android:name=".SettingsInitialize"
             android:exported="true">
             <intent-filter>
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.