Use custom QS fragment for auto use-case.

Use a custom QS fragment if the UI mode is car. This fragment disables
the quick settings row and expansion. Remove the config options that
used to have disabled this in the phone QSFragment.

To accomplish this, introduce a new QSFooter interface. The old
QSFooter.java now implements this.

Test: boot on phone and Android Auto headunit
Bug: 33210494
Change-Id: I5accc2d27d6725380ca5e271d332a03991c9419b
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index d571243..a648345e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -31,9 +31,9 @@
 @DependsOn(target = HeightListener.class)
 public interface QS extends FragmentBase {
 
-    public static final String ACTION = "com.android.systemui.action.PLUGIN_QS";
+    String ACTION = "com.android.systemui.action.PLUGIN_QS";
 
-    public static final int VERSION = 6;
+    int VERSION = 6;
 
     String TAG = "QS";
 
@@ -66,8 +66,8 @@
     }
 
     @ProvidesInterface(version = HeightListener.VERSION)
-    public interface HeightListener {
-        public static final int VERSION = 1;
+    interface HeightListener {
+        int VERSION = 1;
         void onQsHeightChanged();
     }
 
diff --git a/packages/SystemUI/res/drawable/car_qs_background_primary.xml b/packages/SystemUI/res/drawable/car_qs_background_primary.xml
new file mode 100644
index 0000000..0f77987
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_qs_background_primary.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorPrimaryDark"/>
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/SystemUI/res/layout/car_qs_footer.xml
new file mode 100644
index 0000000..2ef3b05
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_qs_footer.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<!-- extends RelativeLayout -->
+<com.android.systemui.qs.car.CarQSFooter
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/qs_footer"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_footer_height"
+    android:baselineAligned="false"
+    android:clickable="false"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:paddingBottom="16dp"
+    android:paddingTop="16dp"
+    android:paddingEnd="32dp"
+    android:paddingStart="32dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+    <com.android.systemui.statusbar.phone.MultiUserSwitch
+        android:id="@+id/multi_user_switch"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:background="@drawable/ripple_drawable"
+        android:focusable="true">
+
+        <ImageView
+            android:id="@+id/multi_user_avatar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:scaleType="centerInside"/>
+    </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+    <com.android.systemui.statusbar.phone.SettingsButton
+        android:id="@+id/settings_button"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:background="@drawable/ripple_drawable"
+        android:contentDescription="@string/accessibility_quick_settings_settings"
+        android:scaleType="centerCrop"
+        android:src="@drawable/ic_settings_16dp"
+        android:tint="?android:attr/colorForeground"
+        style="@android:style/Widget.Material.Button.Borderless" />
+
+</com.android.systemui.qs.car.CarQSFooter>
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
new file mode 100644
index 0000000..d1f7ff8
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/quick_settings_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/car_qs_background_primary"
+    android:orientation="vertical"
+    android:elevation="4dp">
+
+    <include layout="@layout/car_status_bar_header" />
+    <include layout="@layout/car_qs_footer" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
new file mode 100644
index 0000000..158907e
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_status_bar_header.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<!-- Extends RelativeLayout -->
+<com.android.systemui.qs.car.CarStatusBarHeader
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/header"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/car_qs_header_system_icons_area_height"
+    android:paddingStart="8dp"
+    android:paddingEnd="8dp" >
+
+    <include
+        layout="@layout/system_icons"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true" />
+
+    <com.android.systemui.statusbar.policy.Clock
+        android:id="@+id/clock"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:singleLine="true"
+        android:paddingStart="@dimen/status_bar_clock_starting_padding"
+        android:paddingEnd="@dimen/status_bar_clock_end_padding"
+        systemui:showDark="false" />
+</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/qs_footer.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
similarity index 96%
rename from packages/SystemUI/res/layout/qs_footer.xml
rename to packages/SystemUI/res/layout/qs_footer_impl.xml
index db39905..43e88ba 100644
--- a/packages/SystemUI/res/layout/qs_footer.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -15,10 +15,10 @@
 ** limitations under the License.
 -->
 
-<!-- Extends RelativeLayout -->
-<com.android.systemui.qs.QSFooter
+<!-- Extends FrameLayout -->
+<com.android.systemui.qs.QSFooterImpl
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/header"
+    android:id="@+id/qs_footer"
     android:layout_width="match_parent"
     android:layout_height="48dp"
     android:baselineAligned="false"
@@ -114,4 +114,4 @@
             android:padding="14dp" />
     </LinearLayout>
 
-</com.android.systemui.qs.QSFooter>
+</com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 87101e4..5541f3d 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -32,8 +32,7 @@
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
-    <include android:id="@+id/qs_footer"
-        layout="@layout/qs_footer" />
+    <include layout="@layout/qs_footer_impl" />
 
     <include android:id="@+id/qs_detail" layout="@layout/qs_detail" />
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 2ff626a..e8b418c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -18,7 +18,6 @@
 <!-- Extends RelativeLayout -->
 <com.android.systemui.qs.QuickStatusBarHeader
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/header"
     android:layout_width="match_parent"
     android:layout_height="@dimen/status_bar_header_height"
@@ -31,46 +30,7 @@
     android:paddingEnd="0dp"
     android:paddingStart="0dp">
 
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="32dp"
-        android:layout_alignParentEnd="true"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:gravity="center"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
-        android:orientation="horizontal">
-
-
-        <com.android.keyguard.CarrierText
-            android:id="@+id/qs_carrier_text"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:gravity="center_vertical|start"
-            android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary"
-            android:singleLine="true" />
-
-        <com.android.systemui.BatteryMeterView android:id="@+id/battery"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            />
-
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_clock_end_padding"
-            android:gravity="center_vertical|start"
-            systemui:showDark="false"
-            />
-    </LinearLayout>
+    <include layout="@layout/quick_status_bar_header_system_icons" />
 
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
new file mode 100644
index 0000000..c6dbd18
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2017, 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_header_system_icons_area_height"
+    android:layout_alignParentEnd="true"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:gravity="center"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:orientation="horizontal">
+
+
+    <com.android.keyguard.CarrierText
+        android:id="@+id/qs_carrier_text"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center_vertical|start"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary"
+        android:singleLine="true" />
+
+    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        />
+
+    <com.android.systemui.statusbar.policy.Clock
+        android:id="@+id/clock"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:paddingStart="@dimen/status_bar_clock_starting_padding"
+        android:paddingEnd="@dimen/status_bar_clock_end_padding"
+        android:gravity="center_vertical|start"
+        systemui:showDark="false"
+        />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-car/dimens.xml b/packages/SystemUI/res/values-car/dimens.xml
new file mode 100644
index 0000000..b2e7bd1
--- /dev/null
+++ b/packages/SystemUI/res/values-car/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2017, 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.
+*/
+-->
+<resources>
+    <!-- The height of the quick settings footer that holds the user switcher, settings icon,
+         etc. in the car setting.-->
+    <dimen name="qs_footer_height">74dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2ad6f2d..c0068d3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -316,26 +316,6 @@
     <!-- Whether or not a background should be drawn behind a notification. -->
     <bool name="config_drawNotificationBackground">true</bool>
 
-    <!-- Whether or not the edit icon on the quick settings header is shown. -->
-    <bool name="config_showQuickSettingsEditingIcon">true</bool>
-
-    <!-- Whether or not the multi-user switcher should be visible even if the quick settings are
-         not expanded. If there are not multiple users on the system, the switcher will still
-         hide itself. -->
-    <bool name="config_alwaysShowMultiUserSwitcher">false</bool>
-
-    <!-- Whether or not the expand indicator is visible for manually expanding the quick settings
-         panel. -->
-    <bool name="config_showQuickSettingsExpandIndicator">true</bool>
-
-    <!-- Whether or not to display the row of quick settings icons separate from the full quick
-         settings panel. -->
-    <bool name="config_showQuickSettingsRow">true</bool>
-
-    <!-- Whether or not the quick settings should be revealed on an overscroll of the
-         notifications panel. -->
-    <bool name="config_enableQuickSettingsOverscrollExpansion">true</bool>
-
     <!-- Whether or the notifications can be shown and dismissed with a drag. -->
     <bool name="config_enableNotificationShadeDrag">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 93d2072..19bafb7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -189,6 +189,23 @@
     <!-- Height of the status bar header bar -->
     <dimen name="status_bar_header_height">124dp</dimen>
 
+    <!-- Height of the status bar header bar in the car setting. -->
+    <dimen name="car_status_bar_header_height">128dp</dimen>
+
+    <!-- The bottom padding of the status bar header. -->
+    <dimen name="status_bar_header_padding_bottom">48dp</dimen>
+
+    <!-- The height of the container that holds the system icons in the quick settings header. -->
+    <dimen name="qs_header_system_icons_area_height">32dp</dimen>
+
+    <!-- The height of the container that holds the system icons in the quick settings header in the
+         car setting. -->
+    <dimen name="car_qs_header_system_icons_area_height">54dp</dimen>
+
+    <!-- The height of the quick settings footer that holds the user switcher, settings icon,
+         etc. -->
+    <dimen name="qs_footer_height">48dp</dimen>
+
     <!-- Height of the status bar header bar when expanded -->
     <dimen name="status_bar_header_height_expanded">124dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ed57c04..33b5268 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -38,7 +38,7 @@
     protected View mHeader;
     protected float mQsExpansion;
     private QSCustomizer mQSCustomizer;
-    private QSFooter mQSFooter;
+    private View mQSFooter;
     private float mFullElevation;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 488fc03..3f3cea2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -13,426 +13,60 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
-
 package com.android.systemui.qs;
 
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
-
-import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.os.UserManager;
-import android.provider.AlarmClock;
 import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.util.AttributeSet;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.R.dimen;
-import com.android.systemui.R.id;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.TouchAnimator.Listener;
-import com.android.systemui.qs.TouchAnimator.ListenerAdapter;
-import com.android.systemui.statusbar.phone.ExpandableIndicator;
-import com.android.systemui.statusbar.phone.MultiUserSwitch;
-import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
-import com.android.systemui.tuner.TunerService;
+/**
+ * The bottom footer of the quick settings panel.
+ */
+public interface QSFooter {
+    /**
+     * Sets the given {@link QSPanel} to be the one that will display the quick settings.
+     */
+    void setQSPanel(@Nullable QSPanel panel);
 
-public class QSFooter extends FrameLayout implements
-        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
-        SignalCallback {
-    private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
+    /**
+     * Sets whether or not the footer should be visible.
+     *
+     * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE} or {@link View#GONE}.
+     * @see View#setVisibility(int)
+     */
+    void setVisibility(int visibility);
 
-    private ActivityStarter mActivityStarter;
-    private NextAlarmController mNextAlarmController;
-    private UserInfoController mUserInfoController;
-    private SettingsButton mSettingsButton;
-    protected View mSettingsContainer;
+    /**
+     * Sets whether the footer is in an expanded state.
+     */
+    void setExpanded(boolean expanded);
 
-    private TextView mAlarmStatus;
-    private View mAlarmStatusCollapsed;
-    private View mDate;
+    /**
+     * Returns the full height of the footer.
+     */
+    int getHeight();
 
-    private QSPanel mQsPanel;
+    /**
+     * Sets the percentage amount that the quick settings has been expanded.
+     *
+     * @param expansion A value from 1 to 0 that indicates how much the quick settings have been
+     *                  expanded. 1 is fully expanded.
+     */
+    void setExpansion(float expansion);
 
-    private boolean mExpanded;
-    private boolean mAlarmShowing;
+    /**
+     * Sets whether or not this footer should set itself to listen for changes in any callbacks
+     * that it has implemented.
+     */
+    void setListening(boolean listening);
 
-    protected ExpandableIndicator mExpandIndicator;
+    /**
+     * Sets whether or not the keyguard is currently being shown.
+     */
+    void setKeyguardShowing(boolean keyguardShowing);
 
-    private boolean mListening;
-    private AlarmManager.AlarmClockInfo mNextAlarm;
-
-    private boolean mShowEmergencyCallsOnly;
-    protected MultiUserSwitch mMultiUserSwitch;
-    private ImageView mMultiUserAvatar;
-    private boolean mAlwaysShowMultiUserSwitch;
-
-    protected TouchAnimator mSettingsAlpha;
-    private float mExpansionAmount;
-
-    protected View mEdit;
-    private boolean mShowEditIcon;
-    private TouchAnimator mAnimator;
-    private View mDateTimeGroup;
-    private boolean mKeyguardShowing;
-    private TouchAnimator mAlarmAnimator;
-
-    public QSFooter(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        Resources res = getResources();
-
-        mShowEditIcon = res.getBoolean(R.bool.config_showQuickSettingsEditingIcon);
-
-        mEdit = findViewById(android.R.id.edit);
-        mEdit.setVisibility(mShowEditIcon ? VISIBLE : GONE);
-
-        if (mShowEditIcon) {
-            findViewById(android.R.id.edit).setOnClickListener(view ->
-                    Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
-                            mQsPanel.showEdit(view)));
-        }
-
-        mDateTimeGroup = findViewById(id.date_time_alarm_group);
-        mDate = findViewById(R.id.date);
-
-        mExpandIndicator = findViewById(R.id.expand_indicator);
-        mExpandIndicator.setVisibility(
-                res.getBoolean(R.bool.config_showQuickSettingsExpandIndicator)
-                        ? VISIBLE : GONE);
-
-        mSettingsButton = findViewById(R.id.settings_button);
-        mSettingsContainer = findViewById(R.id.settings_button_container);
-        mSettingsButton.setOnClickListener(this);
-
-        mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
-        mAlarmStatus = findViewById(R.id.alarm_status);
-        mDateTimeGroup.setOnClickListener(this);
-
-        mMultiUserSwitch = findViewById(R.id.multi_user_switch);
-        mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
-        mAlwaysShowMultiUserSwitch = res.getBoolean(R.bool.config_alwaysShowMultiUserSwitcher);
-
-        // RenderThread is doing more harm than good when touching the header (to expand quick
-        // settings), so disable it for this view
-        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
-
-        updateResources();
-
-        mNextAlarmController = Dependency.get(NextAlarmController.class);
-        mUserInfoController = Dependency.get(UserInfoController.class);
-        mActivityStarter = Dependency.get(ActivityStarter.class);
-        addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
-                oldBottom) -> updateAnimator(right - left));
-    }
-
-    private void updateAnimator(int width) {
-        int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
-        int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
-                - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
-        int remaining = (width - numTiles * size) / (numTiles - 1);
-        int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
-
-        mAnimator = new Builder()
-                .addFloat(mSettingsContainer, "translationX", -(remaining - defSpace), 0)
-                .addFloat(mSettingsButton, "rotation", -120, 0)
-                .build();
-        if (mAlarmShowing) {
-            mAlarmAnimator = new Builder().addFloat(mDate, "alpha", 1, 0)
-                    .addFloat(mDateTimeGroup, "translationX", 0, -mDate.getWidth())
-                    .addFloat(mAlarmStatus, "alpha", 0, 1)
-                    .setListener(new ListenerAdapter() {
-                        @Override
-                        public void onAnimationAtStart() {
-                            mAlarmStatus.setVisibility(View.GONE);
-                        }
-
-                        @Override
-                        public void onAnimationStarted() {
-                            mAlarmStatus.setVisibility(View.VISIBLE);
-                        }
-                    }).build();
-        } else {
-            mAlarmAnimator = null;
-            mAlarmStatus.setVisibility(View.GONE);
-            mDate.setAlpha(1);
-            mDateTimeGroup.setTranslationX(0);
-        }
-        setExpansion(mExpansionAmount);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateResources();
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        updateResources();
-    }
-
-    private void updateResources() {
-        FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
-
-        updateSettingsAnimator();
-    }
-
-    private void updateSettingsAnimator() {
-        mSettingsAlpha = createSettingsAlphaAnimator();
-
-        final boolean isRtl = isLayoutRtl();
-        if (isRtl && mDate.getWidth() == 0) {
-            mDate.addOnLayoutChangeListener(new OnLayoutChangeListener() {
-                @Override
-                public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                    mDate.setPivotX(getWidth());
-                    mDate.removeOnLayoutChangeListener(this);
-                }
-            });
-        } else {
-            mDate.setPivotX(isRtl ? mDate.getWidth() : 0);
-        }
-    }
-
+    /**
+     * Returns the {@link View} that should expand the quick settings when clicked.
+     */
     @Nullable
-    private TouchAnimator createSettingsAlphaAnimator() {
-        // If the settings icon is not shown and the user switcher is always shown, then there
-        // is nothing to animate.
-        if (!mShowEditIcon && mAlwaysShowMultiUserSwitch) {
-            return null;
-        }
-
-        TouchAnimator.Builder animatorBuilder = new TouchAnimator.Builder();
-        animatorBuilder.setStartDelay(QSAnimator.EXPANDED_TILE_DELAY);
-
-        if (mShowEditIcon) {
-            animatorBuilder.addFloat(mEdit, "alpha", 0, 1);
-        }
-
-        if (!mAlwaysShowMultiUserSwitch) {
-            animatorBuilder.addFloat(mMultiUserSwitch, "alpha", 0, 1);
-        }
-
-        return animatorBuilder.build();
-    }
-
-    public void setKeyguardShowing(boolean keyguardShowing) {
-        mKeyguardShowing = keyguardShowing;
-        setExpansion(mExpansionAmount);
-    }
-
-    public void setExpanded(boolean expanded) {
-        if (mExpanded == expanded) return;
-        mExpanded = expanded;
-        updateEverything();
-    }
-
-    @Override
-    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarm = nextAlarm;
-        if (nextAlarm != null) {
-            String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm);
-            mAlarmStatus.setText(alarmString);
-            mAlarmStatus.setContentDescription(mContext.getString(
-                    R.string.accessibility_quick_settings_alarm, alarmString));
-            mAlarmStatusCollapsed.setContentDescription(mContext.getString(
-                    R.string.accessibility_quick_settings_alarm, alarmString));
-        }
-        if (mAlarmShowing != (nextAlarm != null)) {
-            mAlarmShowing = nextAlarm != null;
-            updateAnimator(getWidth());
-            updateEverything();
-        }
-    }
-
-    public void setExpansion(float headerExpansionFraction) {
-        mExpansionAmount = headerExpansionFraction;
-        if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
-        if (mAlarmAnimator != null) mAlarmAnimator.setPosition(
-                mKeyguardShowing ? 0 : headerExpansionFraction);
-
-        if (mSettingsAlpha != null) {
-            mSettingsAlpha.setPosition(headerExpansionFraction);
-        }
-
-        updateAlarmVisibilities();
-
-        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
-    }
-
-    @Override
-    @VisibleForTesting
-    public void onDetachedFromWindow() {
-        setListening(false);
-        super.onDetachedFromWindow();
-    }
-
-    private void updateAlarmVisibilities() {
-        mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
-    }
-
-    public void setListening(boolean listening) {
-        if (listening == mListening) {
-            return;
-        }
-        mListening = listening;
-        updateListeners();
-    }
-
-    public View getExpandView() {
-        return findViewById(R.id.expand_indicator);
-    }
-
-    public void updateEverything() {
-        post(() -> {
-            updateVisibilities();
-            setClickable(false);
-        });
-    }
-
-    private void updateVisibilities() {
-        updateAlarmVisibilities();
-        mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
-                TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
-        final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
-
-        mMultiUserSwitch.setVisibility((mExpanded || mAlwaysShowMultiUserSwitch)
-                && mMultiUserSwitch.hasMultipleUsers() && !isDemo
-                ? View.VISIBLE : View.INVISIBLE);
-
-        if (mShowEditIcon) {
-            mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
-        }
-    }
-
-    private void updateListeners() {
-        if (mListening) {
-            mNextAlarmController.addCallback(this);
-            mUserInfoController.addCallback(this);
-            if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
-                Dependency.get(NetworkController.class).addEmergencyListener(this);
-                Dependency.get(NetworkController.class).addCallback(this);
-            }
-        } else {
-            mNextAlarmController.removeCallback(this);
-            mUserInfoController.removeCallback(this);
-            Dependency.get(NetworkController.class).removeEmergencyListener(this);
-            Dependency.get(NetworkController.class).removeCallback(this);
-        }
-    }
-
-    public void setQSPanel(final QSPanel qsPanel) {
-        mQsPanel = qsPanel;
-        if (mQsPanel != null) {
-            mMultiUserSwitch.setQsPanel(qsPanel);
-        }
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mSettingsButton) {
-            if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
-                // If user isn't setup just unlock the device and dump them back at SUW.
-                mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
-                return;
-            }
-            MetricsLogger.action(mContext,
-                    mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
-                            : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
-            if (mSettingsButton.isTunerClick()) {
-                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
-                    if (TunerService.isTunerEnabled(mContext)) {
-                        TunerService.showResetRequest(mContext, () -> {
-                            // Relaunch settings so that the tuner disappears.
-                            startSettingsActivity();
-                        });
-                    } else {
-                        Toast.makeText(getContext(), R.string.tuner_toast,
-                                Toast.LENGTH_LONG).show();
-                        TunerService.setTunerEnabled(mContext, true);
-                    }
-                    startSettingsActivity();
-
-                });
-            } else {
-                startSettingsActivity();
-            }
-        } else if (v == mDateTimeGroup) {
-            Dependency.get(MetricsLogger.class).action(ACTION_QS_DATE,
-                    mNextAlarm != null);
-            if (mNextAlarm != null) {
-                PendingIntent showIntent = mNextAlarm.getShowIntent();
-                mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
-            } else {
-                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                        AlarmClock.ACTION_SHOW_ALARMS), 0);
-            }
-        }
-    }
-
-    private void startSettingsActivity() {
-        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
-                true /* dismissShade */);
-    }
-
-    @Override
-    public void setEmergencyCallsOnly(boolean show) {
-        boolean changed = show != mShowEmergencyCallsOnly;
-        if (changed) {
-            mShowEmergencyCallsOnly = show;
-            if (mExpanded) {
-                updateEverything();
-            }
-        }
-    }
-
-    @Override
-    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-        if (picture != null &&
-                UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser())) {
-            picture = picture.getConstantState().newDrawable().mutate();
-            picture.setColorFilter(
-                    Utils.getColorAttr(mContext, android.R.attr.colorForeground),
-                    Mode.SRC_IN);
-        }
-        mMultiUserAvatar.setImageDrawable(picture);
-    }
+    View getExpandView();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
new file mode 100644
index 0000000..94da5f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2017 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.qs;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.UserManager;
+import android.provider.AlarmClock;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
+import com.android.systemui.FontSizeUtils;
+import com.android.systemui.R;
+import com.android.systemui.R.dimen;
+import com.android.systemui.R.id;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.TouchAnimator.Builder;
+import com.android.systemui.qs.TouchAnimator.Listener;
+import com.android.systemui.qs.TouchAnimator.ListenerAdapter;
+import com.android.systemui.statusbar.phone.ExpandableIndicator;
+import com.android.systemui.statusbar.phone.MultiUserSwitch;
+import com.android.systemui.statusbar.phone.SettingsButton;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+import com.android.systemui.tuner.TunerService;
+
+public class QSFooterImpl extends FrameLayout implements QSFooter,
+        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
+        SignalCallback {
+    private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
+
+    private ActivityStarter mActivityStarter;
+    private NextAlarmController mNextAlarmController;
+    private UserInfoController mUserInfoController;
+    private SettingsButton mSettingsButton;
+    protected View mSettingsContainer;
+
+    private TextView mAlarmStatus;
+    private View mAlarmStatusCollapsed;
+    private View mDate;
+
+    private QSPanel mQsPanel;
+
+    private boolean mExpanded;
+    private boolean mAlarmShowing;
+
+    protected ExpandableIndicator mExpandIndicator;
+
+    private boolean mListening;
+    private AlarmManager.AlarmClockInfo mNextAlarm;
+
+    private boolean mShowEmergencyCallsOnly;
+    protected MultiUserSwitch mMultiUserSwitch;
+    private ImageView mMultiUserAvatar;
+
+    protected TouchAnimator mSettingsAlpha;
+    private float mExpansionAmount;
+
+    protected View mEdit;
+    private TouchAnimator mAnimator;
+    private View mDateTimeGroup;
+    private boolean mKeyguardShowing;
+    private TouchAnimator mAlarmAnimator;
+
+    public QSFooterImpl(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        Resources res = getResources();
+
+        mEdit = findViewById(android.R.id.edit);
+        mEdit.setOnClickListener(view ->
+                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
+                        mQsPanel.showEdit(view)));
+
+        mDateTimeGroup = findViewById(id.date_time_alarm_group);
+        mDate = findViewById(R.id.date);
+
+        mExpandIndicator = findViewById(R.id.expand_indicator);
+        mSettingsButton = findViewById(R.id.settings_button);
+        mSettingsContainer = findViewById(R.id.settings_button_container);
+        mSettingsButton.setOnClickListener(this);
+
+        mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
+        mAlarmStatus = findViewById(R.id.alarm_status);
+        mDateTimeGroup.setOnClickListener(this);
+
+        mMultiUserSwitch = findViewById(R.id.multi_user_switch);
+        mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+
+        // RenderThread is doing more harm than good when touching the header (to expand quick
+        // settings), so disable it for this view
+        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
+
+        updateResources();
+
+        mNextAlarmController = Dependency.get(NextAlarmController.class);
+        mUserInfoController = Dependency.get(UserInfoController.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
+        addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
+                oldBottom) -> updateAnimator(right - left));
+    }
+
+    private void updateAnimator(int width) {
+        int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
+        int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+                - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
+        int remaining = (width - numTiles * size) / (numTiles - 1);
+        int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
+
+        mAnimator = new Builder()
+                .addFloat(mSettingsContainer, "translationX", -(remaining - defSpace), 0)
+                .addFloat(mSettingsButton, "rotation", -120, 0)
+                .build();
+        if (mAlarmShowing) {
+            mAlarmAnimator = new Builder().addFloat(mDate, "alpha", 1, 0)
+                    .addFloat(mDateTimeGroup, "translationX", 0, -mDate.getWidth())
+                    .addFloat(mAlarmStatus, "alpha", 0, 1)
+                    .setListener(new ListenerAdapter() {
+                        @Override
+                        public void onAnimationAtStart() {
+                            mAlarmStatus.setVisibility(View.GONE);
+                        }
+
+                        @Override
+                        public void onAnimationStarted() {
+                            mAlarmStatus.setVisibility(View.VISIBLE);
+                        }
+                    }).build();
+        } else {
+            mAlarmAnimator = null;
+            mAlarmStatus.setVisibility(View.GONE);
+            mDate.setAlpha(1);
+            mDateTimeGroup.setTranslationX(0);
+        }
+        setExpansion(mExpansionAmount);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateResources();
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        updateResources();
+    }
+
+    private void updateResources() {
+        FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
+
+        updateSettingsAnimator();
+    }
+
+    private void updateSettingsAnimator() {
+        mSettingsAlpha = createSettingsAlphaAnimator();
+
+        final boolean isRtl = isLayoutRtl();
+        if (isRtl && mDate.getWidth() == 0) {
+            mDate.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    mDate.setPivotX(getWidth());
+                    mDate.removeOnLayoutChangeListener(this);
+                }
+            });
+        } else {
+            mDate.setPivotX(isRtl ? mDate.getWidth() : 0);
+        }
+    }
+
+    @Nullable
+    private TouchAnimator createSettingsAlphaAnimator() {
+        return new TouchAnimator.Builder()
+                .addFloat(mEdit, "alpha", 0, 1)
+                .addFloat(mMultiUserSwitch, "alpha", 0, 1)
+                .build();
+    }
+
+    @Override
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        mKeyguardShowing = keyguardShowing;
+        setExpansion(mExpansionAmount);
+    }
+
+    @Override
+    public void setExpanded(boolean expanded) {
+        if (mExpanded == expanded) return;
+        mExpanded = expanded;
+        updateEverything();
+    }
+
+    @Override
+    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
+        mNextAlarm = nextAlarm;
+        if (nextAlarm != null) {
+            String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm);
+            mAlarmStatus.setText(alarmString);
+            mAlarmStatus.setContentDescription(mContext.getString(
+                    R.string.accessibility_quick_settings_alarm, alarmString));
+            mAlarmStatusCollapsed.setContentDescription(mContext.getString(
+                    R.string.accessibility_quick_settings_alarm, alarmString));
+        }
+        if (mAlarmShowing != (nextAlarm != null)) {
+            mAlarmShowing = nextAlarm != null;
+            updateAnimator(getWidth());
+            updateEverything();
+        }
+    }
+
+    @Override
+    public void setExpansion(float headerExpansionFraction) {
+        mExpansionAmount = headerExpansionFraction;
+        if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
+        if (mAlarmAnimator != null) mAlarmAnimator.setPosition(
+                mKeyguardShowing ? 0 : headerExpansionFraction);
+
+        if (mSettingsAlpha != null) {
+            mSettingsAlpha.setPosition(headerExpansionFraction);
+        }
+
+        updateAlarmVisibilities();
+
+        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
+    }
+
+    @Override
+    @VisibleForTesting
+    public void onDetachedFromWindow() {
+        setListening(false);
+        super.onDetachedFromWindow();
+    }
+
+    private void updateAlarmVisibilities() {
+        mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening == mListening) {
+            return;
+        }
+        mListening = listening;
+        updateListeners();
+    }
+
+    @Override
+    public View getExpandView() {
+        return findViewById(R.id.expand_indicator);
+    }
+
+    public void updateEverything() {
+        post(() -> {
+            updateVisibilities();
+            setClickable(false);
+        });
+    }
+
+    private void updateVisibilities() {
+        updateAlarmVisibilities();
+        mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
+                TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
+        final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
+
+        mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
+                ? View.VISIBLE : View.INVISIBLE);
+
+        mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    private void updateListeners() {
+        if (mListening) {
+            mNextAlarmController.addCallback(this);
+            mUserInfoController.addCallback(this);
+            if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
+                Dependency.get(NetworkController.class).addEmergencyListener(this);
+                Dependency.get(NetworkController.class).addCallback(this);
+            }
+        } else {
+            mNextAlarmController.removeCallback(this);
+            mUserInfoController.removeCallback(this);
+            Dependency.get(NetworkController.class).removeEmergencyListener(this);
+            Dependency.get(NetworkController.class).removeCallback(this);
+        }
+    }
+
+    @Override
+    public void setQSPanel(final QSPanel qsPanel) {
+        mQsPanel = qsPanel;
+        if (mQsPanel != null) {
+            mMultiUserSwitch.setQsPanel(qsPanel);
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mSettingsButton) {
+            if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
+                // If user isn't setup just unlock the device and dump them back at SUW.
+                mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
+                return;
+            }
+            MetricsLogger.action(mContext,
+                    mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+                            : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
+            if (mSettingsButton.isTunerClick()) {
+                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+                    if (TunerService.isTunerEnabled(mContext)) {
+                        TunerService.showResetRequest(mContext, () -> {
+                            // Relaunch settings so that the tuner disappears.
+                            startSettingsActivity();
+                        });
+                    } else {
+                        Toast.makeText(getContext(), R.string.tuner_toast,
+                                Toast.LENGTH_LONG).show();
+                        TunerService.setTunerEnabled(mContext, true);
+                    }
+                    startSettingsActivity();
+
+                });
+            } else {
+                startSettingsActivity();
+            }
+        } else if (v == mDateTimeGroup) {
+            Dependency.get(MetricsLogger.class).action(ACTION_QS_DATE,
+                    mNextAlarm != null);
+            if (mNextAlarm != null) {
+                PendingIntent showIntent = mNextAlarm.getShowIntent();
+                mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
+            } else {
+                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
+                        AlarmClock.ACTION_SHOW_ALARMS), 0);
+            }
+        }
+    }
+
+    private void startSettingsActivity() {
+        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
+                true /* dismissShade */);
+    }
+
+    @Override
+    public void setEmergencyCallsOnly(boolean show) {
+        boolean changed = show != mShowEmergencyCallsOnly;
+        if (changed) {
+            mShowEmergencyCallsOnly = show;
+            if (mExpanded) {
+                updateEverything();
+            }
+        }
+    }
+
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+        if (picture != null &&
+                UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser())) {
+            picture = picture.getConstantState().newDrawable().mutate();
+            picture.setColorFilter(
+                    Utils.getColorAttr(mContext, android.R.attr.colorForeground),
+                    Mode.SRC_IN);
+        }
+        mMultiUserAvatar.setImageDrawable(picture);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bb36725..f9ccb50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -16,11 +16,11 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
 import android.app.Fragment;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -80,14 +80,9 @@
         mFooter = view.findViewById(R.id.qs_footer);
         mContainer = view.findViewById(id.quick_settings_container);
 
-        mQSDetail.setQsPanel(mQSPanel, mHeader, mFooter);
-
-        // If the quick settings row is not shown, then there is no need for the animation from
-        // the row to the full QS panel.
-        if (getResources().getBoolean(R.bool.config_showQuickSettingsRow)) {
-            mQSAnimator = new QSAnimator(this,
-                    mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
-        }
+        mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
+        mQSAnimator = new QSAnimator(this,
+                mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
 
         mQSCustomizer = view.findViewById(R.id.qs_customize);
         mQSCustomizer.setQs(this);
@@ -131,6 +126,7 @@
     public void setHasNotifications(boolean hasNotifications) {
     }
 
+    @Override
     public void setPanelView(HeightListener panelView) {
         mPanelView = panelView;
     }
@@ -154,6 +150,7 @@
         }
     }
 
+    @Override
     public boolean isCustomizing() {
         return mQSCustomizer.isCustomizing();
     }
@@ -195,15 +192,22 @@
         return mQSCustomizer;
     }
 
+    @Override
     public boolean isShowingDetail() {
         return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
     }
 
+    @Override
     public void setHeaderClickable(boolean clickable) {
         if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
-        mFooter.getExpandView().setClickable(clickable);
+
+        View expandView = mFooter.getExpandView();
+        if (expandView != null) {
+            expandView.setClickable(clickable);
+        }
     }
 
+    @Override
     public void setExpanded(boolean expanded) {
         if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
         mQsExpanded = expanded;
@@ -211,6 +215,7 @@
         updateQsState();
     }
 
+    @Override
     public void setKeyguardShowing(boolean keyguardShowing) {
         if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
         mKeyguardShowing = keyguardShowing;
@@ -223,12 +228,14 @@
         updateQsState();
     }
 
+    @Override
     public void setOverscrolling(boolean stackScrollerOverscrolling) {
         if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling);
         mStackScrollerOverscrolling = stackScrollerOverscrolling;
         updateQsState();
     }
 
+    @Override
     public void setListening(boolean listening) {
         if (DEBUG) Log.d(TAG, "setListening " + listening);
         mListening = listening;
@@ -237,11 +244,13 @@
         mQSPanel.setListening(mListening && mQsExpanded);
     }
 
+    @Override
     public void setHeaderListening(boolean listening) {
         mHeader.setListening(listening);
         mFooter.setListening(listening);
     }
 
+    @Override
     public void setQsExpansion(float expansion, float headerTranslation) {
         if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
         mContainer.setExpansion(expansion);
@@ -269,6 +278,7 @@
         mQSPanel.setClipBounds(mQsBounds);
     }
 
+    @Override
     public void animateHeaderSlidingIn(long delay) {
         if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
         // If the QS is already expanded we don't need to slide in the header as it's already
@@ -280,6 +290,7 @@
         }
     }
 
+    @Override
     public void animateHeaderSlidingOut() {
         if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
         mHeaderAnimating = true;
@@ -300,7 +311,11 @@
 
     @Override
     public void setExpandClickListener(OnClickListener onClickListener) {
-        mFooter.getExpandView().setOnClickListener(onClickListener);
+        View expandView = mFooter.getExpandView();
+
+        if (expandView != null) {
+            expandView.setOnClickListener(onClickListener);
+        }
     }
 
     @Override
@@ -323,6 +338,7 @@
      * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that
      * during closing the detail panel, this already returns the smaller height.
      */
+    @Override
     public int getDesiredHeight() {
         if (mQSCustomizer.isCustomizing()) {
             return getView().getHeight();
@@ -342,6 +358,7 @@
         mContainer.setHeightOverride(desiredHeight);
     }
 
+    @Override
     public int getQsMinExpansionHeight() {
         return mHeader.getHeight();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7ec0760..0709e22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -58,8 +58,6 @@
         Resources res = getResources();
 
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
-        mHeaderQsPanel.setVisibility(res.getBoolean(R.bool.config_showQuickSettingsRow)
-                ? VISIBLE : GONE);
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
new file mode 100644
index 0000000..9730f29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.qs.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.QSFooter;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.statusbar.phone.MultiUserSwitch;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+/**
+ * The footer view that displays below the status bar in the auto use-case. This view shows the
+ * user switcher and access to settings.
+ */
+public class CarQSFooter extends RelativeLayout implements QSFooter,
+        UserInfoController.OnUserInfoChangedListener {
+    private UserInfoController mUserInfoController;
+
+    private MultiUserSwitch mMultiUserSwitch;
+    private ImageView mMultiUserAvatar;
+
+    public CarQSFooter(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mMultiUserSwitch = findViewById(R.id.multi_user_switch);
+        mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+
+        mUserInfoController = Dependency.get(UserInfoController.class);
+
+        findViewById(R.id.settings_button).setOnClickListener(v -> {
+            ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);
+
+            if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
+                // If user isn't setup just unlock the device and dump them back at SUW.
+                activityStarter.postQSRunnableDismissingKeyguard(() -> { });
+                return;
+            }
+
+            activityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
+                    true /* dismissShade */);
+        });
+    }
+
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+        mMultiUserAvatar.setImageDrawable(picture);
+    }
+
+    @Override
+    public void setQSPanel(@Nullable QSPanel panel) {
+        if (panel != null) {
+            mMultiUserSwitch.setQsPanel(panel);
+        }
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mUserInfoController.addCallback(this);
+        } else {
+            mUserInfoController.removeCallback(this);
+        }
+    }
+
+    @Nullable
+    @Override
+    public View getExpandView() {
+        // No view that should expand/collapse the quick settings.
+        return null;
+    }
+
+    @Override
+    public void setExpanded(boolean expanded) {
+        // Do nothing because the quick settings cannot be expanded.
+    }
+
+    @Override
+    public void setExpansion(float expansion) {
+        // Do nothing because the quick settings cannot be expanded.
+    }
+
+    @Override
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        // Do nothing because the footer will not be shown when the keyguard is up.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
new file mode 100644
index 0000000..7c2a812
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 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.qs.car;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSFooter;
+
+/**
+ * A quick settings fragment for the car. For auto, there is no row for quick settings or ability
+ * to expand the quick settings panel. Instead, the only thing is that displayed is the
+ * status bar, and a static row with access to the user switcher and settings.
+ */
+public class CarQSFragment extends Fragment implements QS {
+    private View mHeader;
+    private QSFooter mFooter;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.car_qs_panel, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        mHeader = view.findViewById(R.id.header);
+        mFooter = view.findViewById(R.id.qs_footer);
+    }
+
+    @Override
+    public void hideImmediately() {
+        getView().setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    public void setQsExpansion(float qsExpansionFraction, float headerTranslation) {
+        // If the header is to be completed translated down, then set it to be visible.
+        getView().setVisibility(headerTranslation == 0 ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    @Override
+    public View getHeader() {
+        return mHeader;
+    }
+
+    @VisibleForTesting
+    QSFooter getFooter() {
+        return mFooter;
+    }
+
+    @Override
+    public void setHeaderListening(boolean listening) {
+        mFooter.setListening(listening);
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        mFooter.setListening(listening);
+    }
+
+    @Override
+    public int getQsMinExpansionHeight() {
+        return getView().getHeight();
+    }
+
+    @Override
+    public int getDesiredHeight() {
+        return getView().getHeight();
+    }
+
+    @Override
+    public void setPanelView(HeightListener notificationPanelView) {
+        // No quick settings panel.
+    }
+
+    @Override
+    public void setHeightOverride(int desiredHeight) {
+        // No ability to expand quick settings.
+    }
+
+    @Override
+    public void setHeaderClickable(boolean qsExpansionEnabled) {
+        // Usually this sets the expand button to be clickable, but there is no quick settings to
+        // expand.
+    }
+
+    @Override
+    public boolean isCustomizing() {
+        // No ability to customize the quick settings.
+        return false;
+    }
+
+    @Override
+    public void setOverscrolling(boolean overscrolling) {
+        // No overscrolling to reveal quick settings.
+    }
+
+    @Override
+    public void setExpanded(boolean qsExpanded) {
+        // No quick settings to expand
+    }
+
+    @Override
+    public boolean isShowingDetail() {
+        // No detail panel to close.
+        return false;
+    }
+
+    @Override
+    public void closeDetail() {
+        // No detail panel to close.
+    }
+
+    @Override
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        // No keyguard to show.
+    }
+
+    @Override
+    public void animateHeaderSlidingIn(long delay) {
+        // No header to animate.
+    }
+
+    @Override
+    public void animateHeaderSlidingOut() {
+        // No header to animate.
+    }
+
+    @Override
+    public void notifyCustomizeChanged() {
+        // There is no ability to customize quick settings.
+    }
+
+    @Override
+    public void setContainer(ViewGroup container) {
+        // No quick settings, so no container to set.
+    }
+
+    @Override
+    public void setExpandClickListener(OnClickListener onClickListener) {
+        // No ability to expand the quick settings.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
new file mode 100644
index 0000000..6797bb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.qs.car;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.support.annotation.IdRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.BatteryMeterView;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+
+/**
+ * A view that forms the header of the notification panel. This view will ensure that any
+ * status icons that are displayed are tinted accordingly to the current theme.
+ */
+public class CarStatusBarHeader extends RelativeLayout {
+    public CarStatusBarHeader(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        // Set the light/dark theming on the header status UI to match the current theme.
+        int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
+        float intensity = colorForeground == Color.WHITE ? 0f : 1f;
+        Rect tintArea = new Rect(0, 0, 0, 0);
+
+        applyDarkness(R.id.signal_cluster, tintArea, intensity, colorForeground);
+        applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
+        applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+
+        ((BatteryMeterView) findViewById(R.id.battery)).setForceShowPercent(true);
+    }
+
+    private void applyDarkness(@IdRes int id, Rect tintArea, float intensity, int color) {
+        View v = findViewById(id);
+        if (v instanceof DarkIconDispatcher.DarkReceiver) {
+            ((DarkIconDispatcher.DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 4701f85..a773172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -153,7 +153,6 @@
     protected int mQsMinExpansionHeight;
     protected int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
-    private boolean mQsOverscrollExpansionEnabled;
     private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionFromOverscroll;
     private float mLastOverscroll;
@@ -242,8 +241,6 @@
         super(context, attrs);
         setWillNotDraw(!DEBUG);
         mFalsingManager = FalsingManager.getInstance(context);
-        mQsOverscrollExpansionEnabled =
-                getResources().getBoolean(R.bool.config_enableQuickSettingsOverscrollExpansion);
     }
 
     public void setStatusBar(StatusBar bar) {
@@ -668,7 +665,7 @@
             return true;
         }
 
-        if (mQsOverscrollExpansionEnabled && !isFullyCollapsed() && onQsIntercept(event)) {
+        if (!isFullyCollapsed() && onQsIntercept(event)) {
             return true;
         }
         return super.onInterceptTouchEvent(event);
@@ -843,8 +840,7 @@
         }
         handled |= mHeadsUpTouchHelper.onTouchEvent(event);
 
-        if (mQsOverscrollExpansionEnabled && !mHeadsUpTouchHelper.isTrackingHeadsUp()
-                && handleQsTouch(event)) {
+        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
             return true;
         }
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
@@ -1028,10 +1024,6 @@
 
     @Override
     public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
-        if (!mQsOverscrollExpansionEnabled) {
-            return;
-        }
-
         cancelQsAnimation();
         if (!mQsExpansionEnabled) {
             amount = 0f;
@@ -1046,10 +1038,6 @@
 
     @Override
     public void flingTopOverscroll(float velocity, boolean open) {
-        if (!mQsOverscrollExpansionEnabled) {
-            return;
-        }
-
         mLastOverscroll = 0f;
         mQsExpansionFromOverscroll = false;
         setQsExpansion(mQsExpansionHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4d6fd9c..714c287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -182,6 +182,7 @@
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.recents.events.EventBus;
@@ -1149,7 +1150,7 @@
             ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
                     Dependency.get(ExtensionController.class).newExtension(QS.class)
                             .withPlugin(QS.class)
-                            .withUiMode(UI_MODE_TYPE_CAR, () -> new QSFragment())
+                            .withUiMode(UI_MODE_TYPE_CAR, () -> new CarQSFragment())
                             .withDefault(() -> new QSFragment())
                             .build());
             final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index c2618cd..b79137e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -262,7 +262,8 @@
             public UiModeItem(int uiMode, Supplier<T> supplier) {
                 mDesiredUiMode = uiMode;
                 mSupplier = supplier;
-                mUiMode = mDefaultContext.getResources().getConfiguration().uiMode;
+                mUiMode = mDefaultContext.getResources().getConfiguration().uiMode
+                        & Configuration.UI_MODE_TYPE_MASK;
                 Dependency.get(ConfigurationController.class).addCallback(this);
             }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
index d25bbe1..703b4d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
@@ -42,9 +42,9 @@
 @RunWithLooper
 @SmallTest
 @Ignore("failing")
-public class QSFooterTest extends LeakCheckedTest {
+public class QSFooterImplTest extends LeakCheckedTest {
 
-    private QSFooter mFooter;
+    private QSFooterImpl mFooter;
     private ActivityStarter mActivityStarter;
     private DeviceProvisionedController mDeviceProvisionedController;
 
@@ -54,9 +54,9 @@
         mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
         mDeviceProvisionedController = mDependency.injectMockDependency(
                 DeviceProvisionedController.class);
-        TestableLooper.get(this).runWithLooper(() -> {
-            mFooter = (QSFooter) LayoutInflater.from(mContext).inflate(R.layout.qs_footer, null);
-        });
+        TestableLooper.get(this).runWithLooper(
+                () -> mFooter = (QSFooterImpl) LayoutInflater.from(mContext).inflate(
+                        R.layout.qs_footer_impl, null));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
new file mode 100644
index 0000000..4f87b02
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.qs.car;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.keyguard.CarrierText;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.statusbar.policy.Clock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link CarQSFragment}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class CarQsFragmentTest extends SysuiBaseFragmentTest {
+    public CarQsFragmentTest() {
+        super(CarQSFragment.class);
+    }
+
+    @Before
+    public void initDependencies() {
+        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
+                new LayoutInflaterBuilder(mContext)
+                        .replace("com.android.systemui.statusbar.policy.SplitClockView",
+                                FrameLayout.class)
+                        .replace("TextClock", View.class)
+                        .replace(CarrierText.class, View.class)
+                        .replace(Clock.class, View.class)
+                        .build());
+
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
+    }
+
+    @Test
+    public void testLayoutInflation() {
+        CarQSFragment fragment = (CarQSFragment) mFragment;
+        mFragments.dispatchResume();
+
+        assertNotNull(fragment.getHeader());
+        assertNotNull(fragment.getFooter());
+    }
+
+    @Test
+    public void testListening() {
+        CarQSFragment qs = (CarQSFragment) mFragment;
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        qs.setListening(true);
+        processAllMessages();
+
+        qs.setListening(false);
+        processAllMessages();
+    }
+}