Support two pane design in the Settings app.
1. Only enable this feature when the flag is on and library is supported
in this device.
2. Add a placeholder rule for home menu.
Network page will be shown to the right pane by default.
3. Add a rule for most sub settings pages.
Known issues:
1. Transition animation seems wrong in this cl.
2. App is closed if user taps back key on the second layer page in fold
state.
Test: Run the apk on large screen device and regular phone. No crash
happens
Bug: 197716926
Change-Id: I089717e84c5e92c6e5b02d9770a24376e250fea2
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
new file mode 100644
index 0000000..7aa5af8
--- /dev/null
+++ b/src/com/android/settings/SettingsApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.Application;
+
+import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
+
+/** Settings application which sets up activity embedding rules for the large screen device. */
+public class SettingsApplication extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final ActivityEmbeddingRulesController controller =
+ new ActivityEmbeddingRulesController(this);
+ controller.initRules();
+ }
+}
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
new file mode 100644
index 0000000..f1a1ecd
--- /dev/null
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.activityembedding;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.LayoutDirection;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.embedding.ActivityFilter;
+import androidx.window.embedding.SplitController;
+import androidx.window.embedding.SplitPairFilter;
+import androidx.window.embedding.SplitPairRule;
+import androidx.window.embedding.SplitPlaceholderRule;
+
+import com.android.settings.Settings;
+import com.android.settings.SubSettings;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** A class to initialize split rules for activity embedding. */
+public class ActivityEmbeddingRulesController {
+
+ private static final String TAG = "ActivityEmbeddingCtrl ";
+ private final Context mContext;
+ private final SplitController mSplitController;
+
+ public ActivityEmbeddingRulesController(Context context) {
+ mContext = context;
+ mSplitController = new SplitController(context);
+ }
+
+ /**
+ * Set up embedding rules to place activities to the right pane.
+ */
+ public void initRules() {
+ if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) {
+ Log.d(TAG, "Not support this feature now");
+ return;
+ }
+
+ mSplitController.clearRegisteredRules();
+
+ // Set a placeholder for home page.
+ mSplitController.registerRule(getHomepagePlaceholderRule());
+ // Set subsettings rule.
+ mSplitController.registerRule(getSubSettingsPairRule());
+ }
+
+ private SplitPlaceholderRule getHomepagePlaceholderRule() {
+ final Set<ActivityFilter> activityFilters = new HashSet<>();
+ activityFilters.add(new ActivityFilter(getComponentName(Settings.class)));
+ final Intent intent = new Intent();
+ intent.setComponent(getComponentName(Settings.NetworkDashboardActivity.class));
+ final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule(
+ activityFilters,
+ intent,
+ ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(mContext),
+ ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(mContext),
+ 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;
+ }
+
+ @NonNull
+ private ComponentName getComponentName(Class<? extends Activity> activityClass) {
+ return new ComponentName(mContext.getPackageName(), activityClass.getName());
+ }
+}
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
new file mode 100644
index 0000000..96c3777
--- /dev/null
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.activityembedding;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.window.embedding.SplitController;
+
+/** An util class collecting all common methods for the embedding activity features. */
+public class ActivityEmbeddingUtils {
+ public static final float SPLIT_RATIO = 0.5f;
+ // The smallest value of current width of the window when the split should be used.
+ private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 600f;
+ // The smallest value of the smallest-width (sw) of the window in any rotation when
+ // the split should be used.
+ private static final float MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600f;
+ private static final String TAG = "ActivityEmbeddingUtils";
+
+ /** Get the smallest pixel value of width of the window when the split should be used. */
+ public static int getMinCurrentScreenSplitWidthPx(Context context) {
+ final DisplayMetrics dm = context.getResources().getDisplayMetrics();
+ return (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP, dm);
+ }
+
+ /**
+ * Get the smallest pixel value of the smallest-width (sw) of the window in any rotation when
+ * the split should be used.
+ */
+ public static int getMinSmallestScreenSplitWidthPx(Context context) {
+ final DisplayMetrics dm = context.getResources().getDisplayMetrics();
+ return (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP, dm);
+ }
+
+ /** Whether to support embedding activity feature. */
+ public static boolean isEmbeddingActivityEnabled(Context context) {
+ final boolean isFlagEnabled = FeatureFlagUtils.isEnabled(context,
+ FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN);
+ final boolean isSplitSupported = new SplitController(context).isSplitSupported();
+
+ Log.d(TAG, "isFlagEnabled = " + isFlagEnabled);
+ Log.d(TAG, "isSplitSupported = " + isSplitSupported);
+
+ return isFlagEnabled && isSplitSupported;
+ }
+}