Add bottom right chip to dream overlay to show glanceable hub

This is added temporarily behind a flag for UX testing purposes and is
not intended to be a proper, full implementation.

Chip can be moved to the bottom left, replacing the home controls
button, by changing the system setting
open_hub_chip_replace_home_controls to 1.

Bug: 339667383
Test: N/A
Flag: com.android.systemui.glanceable_hub_shortcut_button
Change-Id: I34ef11c5d764ca46aea074b14d19f2ac804e3941
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 36bad5e..cb9e6aa 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -990,6 +990,13 @@
 }
 
 flag {
+  name: "glanceable_hub_shortcut_button"
+  namespace: "systemui"
+  description: "Shows a button over the dream and lock screen to open the glanceable hub"
+  bug: "339667383"
+}
+
+flag {
   name: "glanceable_hub_gesture_handle"
   namespace: "systemui"
   description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
diff --git a/packages/SystemUI/res/drawable/ic_widgets.xml b/packages/SystemUI/res/drawable/ic_widgets.xml
new file mode 100644
index 0000000..b21d047
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_widgets.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<!-- go/gm2-icons, from gs_widgets_vd_theme_24.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M666,520L440,294L666,68L892,294L666,520ZM120,440L120,120L440,120L440,440L120,440ZM520,840L520,520L840,520L840,840L520,840ZM120,840L120,520L440,520L440,840L120,840ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM600,760L760,760L760,600L600,600L600,760ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600L600,600Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
new file mode 100644
index 0000000..be063a9
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+-->
+<com.android.systemui.animation.view.LaunchableImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="@dimen/dream_overlay_bottom_affordance_height"
+    android:layout_width="@dimen/dream_overlay_bottom_affordance_width"
+    android:layout_gravity="bottom|start"
+    android:padding="@dimen/dream_overlay_bottom_affordance_padding"
+    android:scaleType="fitCenter"
+    android:tint="?android:attr/textColorPrimary"
+    android:src="@drawable/ic_widgets"
+    android:contentDescription="@string/accessibility_action_open_communal_hub" />
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index afa2375..e284bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
 import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
@@ -35,6 +36,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.controls.ControlsServiceInfo;
@@ -89,6 +91,7 @@
         private final DreamHomeControlsComplication mComplication;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final ControlsComponent mControlsComponent;
+        private final boolean mReplacedByOpenHub;
 
         private boolean mOverlayActive = false;
 
@@ -116,11 +119,13 @@
         public Registrant(DreamHomeControlsComplication complication,
                 DreamOverlayStateController dreamOverlayStateController,
                 ControlsComponent controlsComponent,
-                @SystemUser Monitor monitor) {
+                @SystemUser Monitor monitor,
+                @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub) {
             super(monitor);
             mComplication = complication;
             mControlsComponent = controlsComponent;
             mDreamOverlayStateController = dreamOverlayStateController;
+            mReplacedByOpenHub = replacedByOpenHub;
         }
 
         @Override
@@ -132,7 +137,9 @@
 
         private void updateHomeControlsComplication() {
             mControlsComponent.getControlsListingController().ifPresent(c -> {
-                if (isHomeControlsAvailable(c.getCurrentServices())) {
+                final boolean replacedWithOpenHub =
+                        Flags.glanceableHubShortcutButton() && mReplacedByOpenHub;
+                if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) {
                     mDreamOverlayStateController.addComplication(mComplication);
                 } else {
                     mDreamOverlayStateController.removeComplication(mComplication);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
new file mode 100644
index 0000000..3cf22b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 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.systemui.complication;
+
+import static com.android.systemui.complication.dagger.OpenHubComplicationComponent.OpenHubModule.OPEN_HUB_CHIP_VIEW;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_LAYOUT_PARAMS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalScenes;
+import com.android.systemui.complication.dagger.OpenHubComplicationComponent;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A dream complication that shows a chip to open the glanceable hub.
+ */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+public class OpenHubComplication implements Complication {
+    private final Resources mResources;
+    private final OpenHubComplicationComponent.Factory mComponentFactory;
+
+    @Inject
+    public OpenHubComplication(
+            @Main Resources resources,
+            OpenHubComplicationComponent.Factory componentFactory) {
+        mResources = resources;
+        mComponentFactory = componentFactory;
+    }
+
+    @Override
+    public ViewHolder createView(ComplicationViewModel model) {
+        return mComponentFactory.create(mResources).getViewHolder();
+    }
+
+    @Override
+    public int getRequiredTypeAvailability() {
+        // TODO(b/339667383): create a new complication type if we decide to productionize this
+        return COMPLICATION_TYPE_HOME_CONTROLS;
+    }
+
+    /**
+     * {@link CoreStartable} for registering the complication with SystemUI on startup.
+     */
+    public static class Registrant extends ConditionalCoreStartable {
+        private final OpenHubComplication mComplication;
+        private final DreamOverlayStateController mDreamOverlayStateController;
+
+        private boolean mOverlayActive = false;
+
+        private final DreamOverlayStateController.Callback mOverlayStateCallback =
+                new DreamOverlayStateController.Callback() {
+                    @Override
+                    public void onStateChanged() {
+                        if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
+                            return;
+                        }
+
+                        mOverlayActive = !mOverlayActive;
+
+                        if (mOverlayActive) {
+                            updateOpenHubComplication();
+                        }
+                    }
+                };
+
+        @Inject
+        public Registrant(OpenHubComplication complication,
+                DreamOverlayStateController dreamOverlayStateController,
+                @SystemUser Monitor monitor) {
+            super(monitor);
+            mComplication = complication;
+            mDreamOverlayStateController = dreamOverlayStateController;
+        }
+
+        @Override
+        public void onStart() {
+            mDreamOverlayStateController.addCallback(mOverlayStateCallback);
+        }
+
+        private void updateOpenHubComplication() {
+            // TODO(b/339667383): don't show the complication if glanceable hub is disabled
+            if (Flags.glanceableHubShortcutButton()) {
+                mDreamOverlayStateController.addComplication(mComplication);
+            } else {
+                mDreamOverlayStateController.removeComplication(mComplication);
+            }
+        }
+    }
+
+    /**
+     * Contains values/logic associated with the dream complication view.
+     */
+    public static class OpenHubChipViewHolder implements ViewHolder {
+        private final ImageView mView;
+        private final ComplicationLayoutParams mLayoutParams;
+        private final OpenHubChipViewController mViewController;
+
+        @Inject
+        OpenHubChipViewHolder(
+                OpenHubChipViewController dreamOpenHubChipViewController,
+                @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+                @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
+        ) {
+            mView = view;
+            mLayoutParams = layoutParams;
+            mViewController = dreamOpenHubChipViewController;
+            mViewController.init();
+        }
+
+        @Override
+        public ImageView getView() {
+            return mView;
+        }
+
+        @Override
+        public ComplicationLayoutParams getLayoutParams() {
+            return mLayoutParams;
+        }
+    }
+
+    /**
+     * Controls behavior of the dream complication.
+     */
+    static class OpenHubChipViewController extends ViewController<ImageView> {
+        private static final String TAG = "OpenHubCtrl";
+        private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+        private final Context mContext;
+        private final ConfigurationController mConfigurationController;
+
+        private final ConfigurationController.ConfigurationListener mConfigurationListener =
+                new ConfigurationController.ConfigurationListener() {
+                    @Override
+                    public void onUiModeChanged() {
+                        reloadResources();
+                    }
+                };
+        private final CommunalInteractor mCommunalInteractor;
+
+        @Inject
+        OpenHubChipViewController(
+                @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+                Context context,
+                ConfigurationController configurationController,
+                CommunalInteractor communalInteractor) {
+            super(view);
+
+            mContext = context;
+            mConfigurationController = configurationController;
+            mCommunalInteractor = communalInteractor;
+        }
+
+        @Override
+        protected void onViewAttached() {
+            reloadResources();
+            mView.setOnClickListener(this::onClickOpenHub);
+            mConfigurationController.addCallback(mConfigurationListener);
+        }
+
+        @Override
+        protected void onViewDetached() {
+            mConfigurationController.removeCallback(mConfigurationListener);
+        }
+
+        private void reloadResources() {
+            mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+            final Drawable background = mView.getBackground();
+            if (background != null) {
+                background.setTintList(
+                        Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
+            }
+        }
+
+        private void onClickOpenHub(View v) {
+            if (DEBUG) Log.d(TAG, "open hub complication tapped");
+
+            mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
new file mode 100644
index 0000000..501601e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 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.systemui.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+
+import com.android.systemui.complication.OpenHubComplication;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+/**
+ * Responsible for generating dependencies for the {@link OpenHubComplication}.
+ */
+@Subcomponent(modules = OpenHubComplicationComponent.OpenHubModule.class)
+@OpenHubComplicationComponent.OpenHubComplicationScope
+public interface OpenHubComplicationComponent {
+    /**
+     * Creates a view holder for the open hub complication.
+     */
+    OpenHubComplication.OpenHubChipViewHolder getViewHolder();
+
+    /**
+     * Scope of the open hub complication.
+     */
+    @Documented
+    @Retention(RUNTIME)
+    @Scope
+    @interface OpenHubComplicationScope {
+    }
+
+    /**
+     * Factory that generates a {@link OpenHubComplicationComponent}.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        /**
+         * Creates an instance of {@link OpenHubComplicationComponent}.
+         */
+        OpenHubComplicationComponent create(@BindsInstance Resources resources);
+    }
+
+    /**
+     * Scoped injected values for the {@link OpenHubComplicationComponent}.
+     */
+    @Module
+    interface OpenHubModule {
+        String OPEN_HUB_CHIP_VIEW = "open_hub_chip_view";
+        String OPEN_HUB_BACKGROUND_DRAWABLE = "open_hub_background_drawable";
+
+        /**
+         * Provides the dream open hub chip view.
+         */
+        @Provides
+        @OpenHubComplicationScope
+        @Named(OPEN_HUB_CHIP_VIEW)
+        static ImageView provideOpenHubChipView(
+                LayoutInflater layoutInflater,
+                @Named(OPEN_HUB_BACKGROUND_DRAWABLE) Drawable backgroundDrawable) {
+            final ImageView chip =
+                    (ImageView) layoutInflater.inflate(R.layout.dream_overlay_open_hub_chip,
+                            null, false);
+            chip.setBackground(backgroundDrawable);
+
+            return chip;
+        }
+
+        /**
+         * Provides the background drawable for the open hub chip.
+         */
+        @Provides
+        @OpenHubComplicationScope
+        @Named(OPEN_HUB_BACKGROUND_DRAWABLE)
+        static Drawable providesOpenHubBackground(Context context, Resources resources) {
+            return new DoubleShadowIconDrawable(createShadowInfo(
+                    resources,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dy,
+                    R.dimen.dream_overlay_bottom_affordance_key_shadow_alpha
+            ),
+                    createShadowInfo(
+                            resources,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_radius,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dx,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dy,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_shadow_alpha
+                    ),
+                    resources.getDrawable(R.drawable.dream_overlay_bottom_affordance_bg),
+                    resources.getDimensionPixelOffset(
+                            R.dimen.dream_overlay_bottom_affordance_width),
+                    resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
+            );
+        }
+
+        private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
+                int blurId, int offsetXId, int offsetYId, int alphaId) {
+
+            return new DoubleShadowTextHelper.ShadowInfo(
+                    resources.getDimension(blurId),
+                    resources.getDimension(offsetXId),
+                    resources.getDimension(offsetYId),
+                    resources.getFloat(alphaId)
+            );
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b098..edb5ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -25,6 +25,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
+import com.android.systemui.util.settings.SystemSettings;
 
 import dagger.Module;
 import dagger.Provides;
@@ -39,6 +40,7 @@
         subcomponents = {
                 DreamClockTimeComplicationComponent.class,
                 DreamHomeControlsComplicationComponent.class,
+                OpenHubComplicationComponent.class,
                 DreamMediaEntryComplicationComponent.class
         })
 public interface RegisteredComplicationsModule {
@@ -46,6 +48,8 @@
     String DREAM_SMARTSPACE_LAYOUT_PARAMS = "smartspace_layout_params";
     String DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS = "home_controls_chip_layout_params";
     String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
+    String OPEN_HUB_CHIP_LAYOUT_PARAMS = "open_hub_chip_layout_params";
+    String OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS = "open_hub_chip_replace_home_controls";
 
     int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
     int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT_NO_SMARTSPACE = 2;
@@ -109,6 +113,26 @@
     }
 
     /**
+     * Provides layout parameters for the open hub complication.
+     */
+    @Provides
+    @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS)
+    static ComplicationLayoutParams provideOpenHubLayoutParams(
+            @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replaceHomeControls) {
+        int position = ComplicationLayoutParams.POSITION_BOTTOM | (replaceHomeControls
+                ? ComplicationLayoutParams.POSITION_START
+                : ComplicationLayoutParams.POSITION_END);
+        int direction = replaceHomeControls ? ComplicationLayoutParams.DIRECTION_END
+                : ComplicationLayoutParams.DIRECTION_START;
+        return new ComplicationLayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                position,
+                direction,
+                DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+    }
+
+    /**
      * Provides layout parameters for the smartspace complication.
      */
     @Provides
@@ -124,4 +148,14 @@
                 res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
                 res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
     }
+
+    /**
+     * If true, the home controls chip should not be shown and the open hub chip should be shown in
+     * its place.
+     */
+    @Provides
+    @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS)
+    static boolean providesOpenHubChipReplaceHomeControls(SystemSettings systemSettings) {
+        return systemSettings.getBool(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS, false);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 07c980b..18bd960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -132,7 +132,7 @@
     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -145,7 +145,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -158,7 +158,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -171,7 +171,7 @@
     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(true);
@@ -184,7 +184,7 @@
     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(true);
@@ -197,7 +197,7 @@
     public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setServiceAvailable(true);