Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22ce392b authored by Arc Wang's avatar Arc Wang
Browse files

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
parent ebdc0e26
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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>
+23 −0
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ import com.android.settings.core.SettingsBaseActivity;
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 @@ public class SettingsActivity extends SettingsBaseActivity
        // 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 @@ public class SettingsActivity extends SettingsBaseActivity
        }
    }

    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) {
+26 −24
Original line number Diff line number Diff line
@@ -60,12 +60,34 @@ public class ActivityEmbeddingRulesController {
        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 @@ public class ActivityEmbeddingRulesController {
                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
+80 −0
Original line number Diff line number Diff line
@@ -18,8 +18,14 @@ package com.android.settings.homepage;

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.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.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 @@ public class SettingsHomepageActivity extends FragmentActivity implements
        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 @@ public class SettingsHomepageActivity extends FragmentActivity implements
        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.