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
This commit is contained in:
@@ -107,6 +107,8 @@
|
|||||||
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
|
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
|
<uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />
|
<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
|
<application
|
||||||
android:name=".SettingsApplication"
|
android:name=".SettingsApplication"
|
||||||
@@ -172,6 +174,17 @@
|
|||||||
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
|
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
|
||||||
</activity-alias>
|
</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"
|
<receiver android:name=".SettingsInitialize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@@ -58,6 +58,8 @@ import com.android.settings.core.SettingsBaseActivity;
|
|||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.core.gateway.SettingsGateway;
|
import com.android.settings.core.gateway.SettingsGateway;
|
||||||
import com.android.settings.dashboard.DashboardFeatureProvider;
|
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.homepage.TopLevelSettings;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.wfd.WifiDisplaySettings;
|
import com.android.settings.wfd.WifiDisplaySettings;
|
||||||
@@ -240,7 +242,22 @@ public class SettingsActivity extends SettingsBaseActivity
|
|||||||
// Should happen before any call to getIntent()
|
// Should happen before any call to getIntent()
|
||||||
getMetaData();
|
getMetaData();
|
||||||
|
|
||||||
|
// If it's a deep link intent, start the Activity from SettingsHomepageActivity for 2-pane.
|
||||||
final Intent intent = getIntent();
|
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)) {
|
if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
|
||||||
getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
|
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. */
|
/** Returns the initial fragment name that the activity will launch. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public String getInitialFragmentName(Intent intent) {
|
public String getInitialFragmentName(Intent intent) {
|
||||||
|
@@ -60,12 +60,34 @@ public class ActivityEmbeddingRulesController {
|
|||||||
mSplitController.clearRegisteredRules();
|
mSplitController.clearRegisteredRules();
|
||||||
|
|
||||||
// Set a placeholder for home page.
|
// Set a placeholder for home page.
|
||||||
mSplitController.registerRule(getHomepagePlaceholderRule());
|
registerHomepagePlaceholderRule();
|
||||||
// Set subsettings rule.
|
// 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<>();
|
final Set<ActivityFilter> activityFilters = new HashSet<>();
|
||||||
activityFilters.add(new ActivityFilter(getComponentName(Settings.class)));
|
activityFilters.add(new ActivityFilter(getComponentName(Settings.class)));
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
@@ -78,27 +100,7 @@ public class ActivityEmbeddingRulesController {
|
|||||||
ActivityEmbeddingUtils.SPLIT_RATIO,
|
ActivityEmbeddingUtils.SPLIT_RATIO,
|
||||||
LayoutDirection.LOCALE);
|
LayoutDirection.LOCALE);
|
||||||
|
|
||||||
return placeholderRule;
|
mSplitController.registerRule(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@@ -18,8 +18,14 @@ package com.android.settings.homepage;
|
|||||||
|
|
||||||
import android.animation.LayoutTransition;
|
import android.animation.LayoutTransition;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.FeatureFlagUtils;
|
import android.util.FeatureFlagUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -31,21 +37,33 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
import androidx.window.embedding.SplitController;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.accounts.AvatarViewMixin;
|
import com.android.settings.accounts.AvatarViewMixin;
|
||||||
import com.android.settings.core.CategoryMixin;
|
import com.android.settings.core.CategoryMixin;
|
||||||
import com.android.settings.core.FeatureFlags;
|
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.homepage.contextualcards.ContextualCardsFragment;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
|
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
/** Settings homepage activity */
|
/** Settings homepage activity */
|
||||||
public class SettingsHomepageActivity extends FragmentActivity implements
|
public class SettingsHomepageActivity extends FragmentActivity implements
|
||||||
CategoryMixin.CategoryHandler {
|
CategoryMixin.CategoryHandler {
|
||||||
|
|
||||||
private static final String TAG = "SettingsHomepageActivity";
|
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 static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300;
|
||||||
|
|
||||||
private View mHomepageView;
|
private View mHomepageView;
|
||||||
@@ -105,6 +123,20 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
|||||||
showFragment(new TopLevelSettings(), R.id.main_content);
|
showFragment(new TopLevelSettings(), R.id.main_content);
|
||||||
((FrameLayout) findViewById(R.id.main_content))
|
((FrameLayout) findViewById(R.id.main_content))
|
||||||
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
.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() {
|
private void showSuggestionFragment() {
|
||||||
@@ -141,6 +173,54 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
|||||||
fragmentTransaction.commit();
|
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() {
|
private void initHomepageContainer() {
|
||||||
final View view = findViewById(R.id.homepage_container);
|
final View view = findViewById(R.id.homepage_container);
|
||||||
// Prevent inner RecyclerView gets focus and invokes scrolling.
|
// Prevent inner RecyclerView gets focus and invokes scrolling.
|
||||||
|
Reference in New Issue
Block a user