New keyguard slice data structure

Using androidx package and new API, also splitting content into
multiple views for better animation support.

Bug: 64155983
Test: visual, see data from provider propagate to AoD
Change-Id: I74b5511d582e7ec1f6ffe5dbc5595f54b9ccb202
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index acac671..2f86514 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5778,6 +5778,14 @@
             "touch_exploration_granted_accessibility_services";
 
         /**
+         * Uri of the slice that's presented on the keyguard.
+         * Defaults to a slice with the date and next alarm.
+         *
+         * @hide
+         */
+        public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri";
+
+        /**
          * Whether to speak passwords while in accessibility mode.
          *
          * @deprecated The speaking of passwords is controlled by individual accessibility services.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index eef9866..fc86500 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -525,7 +525,8 @@
                  Settings.Secure.VOICE_INTERACTION_SERVICE,
                  Settings.Secure.VOICE_RECOGNITION_SERVICE,
                  Settings.Secure.INSTANT_APPS_ENABLED,
-                 Settings.Secure.BACKUP_MANAGER_CONSTANTS);
+                 Settings.Secure.BACKUP_MANAGER_CONSTANTS,
+                 Settings.Secure.KEYGUARD_SLICE_URI);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2c5eb27..73fcdd7 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -39,7 +39,12 @@
     android-support-v7-mediarouter \
     android-support-v7-palette \
     android-support-v14-preference \
-    android-support-v17-leanback
+    android-support-v17-leanback \
+    android-slices-core \
+    android-slices-view \
+    android-slices-builders \
+    apptoolkit-arch-core-runtime \
+    apptoolkit-lifecycle-extensions \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-tags \
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 020cfee..b154d46 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -24,31 +24,20 @@
     android:layout_marginEnd="16dp"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/date_owner_info_margin"
     android:layout_gravity="center_horizontal"
-    android:paddingTop="4dp"
     android:clipToPadding="false"
     android:orientation="vertical"
     android:layout_centerHorizontal="true">
     <TextView android:id="@+id/title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:singleLine="true"
-              android:ellipsize="end"
-              android:fadingEdge="horizontal"
-              android:gravity="center"
-              android:textSize="22sp"
-              android:textColor="?attr/wallpaperTextColor"
+              android:layout_marginBottom="@dimen/widget_vertical_padding"
+              android:theme="@style/TextAppearance.Keyguard"
     />
-    <TextView android:id="@+id/text"
+    <LinearLayout android:id="@+id/row"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:singleLine="true"
+              android:orientation="horizontal"
               android:gravity="center"
-              android:visibility="gone"
-              android:textSize="16sp"
-              android:textColor="?attr/wallpaperTextColor"
-              android:layout_marginTop="4dp"
-              android:ellipsize="end"
     />
 </com.android.keyguard.KeyguardSliceView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 138733e..c97cfc4 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -34,6 +34,7 @@
         android:orientation="vertical">
         <RelativeLayout
             android:id="@+id/keyguard_clock_container"
+            android:animateLayoutChanges="true"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal|top">
@@ -59,14 +60,23 @@
                 android:layout_toEndOf="@id/clock_view"
                 android:visibility="invisible"
                 android:src="@drawable/ic_aod_charging_24dp"
-                android:contentDescription="@string/accessibility_ambient_display_charging"
-            />
+                android:contentDescription="@string/accessibility_ambient_display_charging" />
+            <View
+                android:id="@+id/clock_separator"
+                android:layout_width="16dp"
+                android:layout_height="1dp"
+                android:layout_marginTop="10dp"
+                android:layout_below="@id/clock_view"
+                android:background="#f00"
+                android:layout_centerHorizontal="true" />
 
             <include layout="@layout/keyguard_status_area"
                 android:id="@+id/keyguard_status_area"
+                android:layout_marginTop="10dp"
+                android:layout_marginBottom="@dimen/widget_vertical_padding"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@id/clock_view" />
+                android:layout_below="@id/clock_separator" />
         </RelativeLayout>
 
         <TextView
@@ -83,6 +93,5 @@
             android:letterSpacing="0.05"
             android:ellipsize="marquee"
             android:singleLine="true" />
-
     </LinearLayout>
 </com.android.keyguard.KeyguardStatusView>
diff --git a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml
index 1b6fa4c..3fb86d0 100644
--- a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml
@@ -16,5 +16,5 @@
   -->
 
 <resources>
-    <dimen name="widget_big_font_size">72dp</dimen>
+    <dimen name="widget_big_font_size">64dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
index 1b6fa4c..3fb86d0 100644
--- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
@@ -16,5 +16,5 @@
   -->
 
 <resources>
-    <dimen name="widget_big_font_size">72dp</dimen>
+    <dimen name="widget_big_font_size">64dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index bcac072..463af61 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,9 +42,22 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Default clock parameters -->
-    <dimen name="bottom_text_spacing_digital">-1dp</dimen>
-    <dimen name="widget_label_font_size">14sp</dimen>
-    <dimen name="widget_big_font_size">72dp</dimen>
+    <dimen name="bottom_text_spacing_digital">-10dp</dimen>
+    <!-- Slice header -->
+    <dimen name="widget_title_font_size">28sp</dimen>
+    <!-- Slice subtitle  -->
+    <dimen name="widget_label_font_size">16sp</dimen>
+    <!-- Clock without header -->
+    <dimen name="widget_big_font_size">64dp</dimen>
+    <!-- Clock with header -->
+    <dimen name="widget_small_font_size">22dp</dimen>
+    <!-- Dash between clock and header -->
+    <dimen name="widget_vertical_padding">16dp</dimen>
+    <!-- Subtitle paddings -->
+    <dimen name="widget_separator_thickness">2dp</dimen>
+    <dimen name="widget_horizontal_padding">8dp</dimen>
+    <dimen name="widget_icon_size">16dp</dimen>
+    <dimen name="widget_icon_padding">4dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 826e3ea..d50bab5 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -78,4 +78,18 @@
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
     </style>
 
+    <style name="TextAppearance.Keyguard" parent="Theme.SystemUI">
+        <item name="android:textSize">@dimen/widget_title_font_size</item>
+        <item name="android:gravity">center</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:maxLines">2</item>
+    </style>
+
+    <style name="TextAppearance.Keyguard.Secondary">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textSize">@dimen/widget_label_font_size</item>
+        <item name="android:singleLine">true</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index cb3d59c..b9bf80d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -17,38 +17,56 @@
 package com.android.keyguard;
 
 import android.app.PendingIntent;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
 import android.content.Context;
-import android.database.ContentObserver;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Handler;
+import android.provider.Settings;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.tuner.TunerService;
 
-import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.widget.SliceLiveData;
 
 /**
  * View visible under the clock on the lock screen and AoD.
  */
-public class KeyguardSliceView extends LinearLayout {
+public class KeyguardSliceView extends LinearLayout implements View.OnClickListener,
+        Observer<Slice>, TunerService.Tunable {
 
-    private final Uri mKeyguardSliceUri;
+    private static final String TAG = "KeyguardSliceView";
+    private final HashMap<View, PendingIntent> mClickActions;
+    private Uri mKeyguardSliceUri;
     private TextView mTitle;
-    private TextView mText;
-    private Slice mSlice;
-    private PendingIntent mSliceAction;
+    private LinearLayout mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
 
-    private final ContentObserver mObserver;
+    private LiveData<Slice> mLiveData;
+    private int mIconSize;
+    private Consumer<Boolean> mListener;
+    private boolean mHasHeader;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -60,16 +78,20 @@
 
     public KeyguardSliceView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mObserver = new KeyguardSliceObserver(new Handler());
-        mKeyguardSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);;
+
+        TunerService tunerService = Dependency.get(TunerService.class);
+        tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
+
+        mClickActions = new HashMap<>();
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTitle = findViewById(R.id.title);
-        mText = findViewById(R.id.text);
-        mTextColor = mTitle.getCurrentTextColor();
+        mRow = findViewById(R.id.row);
+        mTextColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor);
+        mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
     }
 
     @Override
@@ -77,57 +99,103 @@
         super.onAttachedToWindow();
 
         // Set initial content
-        showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
-                Collections.emptyList()));
+        showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri));
 
         // Make sure we always have the most current slice
-        getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri,
-                false /* notifyDescendants */, mObserver);
+        mLiveData.observeForever(this);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
-        getContext().getContentResolver().unregisterContentObserver(mObserver);
+        mLiveData.removeObserver(this);
     }
 
     private void showSlice(Slice slice) {
-        // Items will be wrapped into an action when they have tap targets.
-        SliceItem actionSlice = SliceQuery.find(slice, SliceItem.FORMAT_ACTION);
-        if (actionSlice != null) {
-            mSlice = actionSlice.getSlice();
-            mSliceAction = actionSlice.getAction();
-        } else {
-            mSlice = slice;
-            mSliceAction = null;
-        }
 
-        if (mSlice == null) {
-            setVisibility(GONE);
-            return;
-        }
+        // Main area
+        SliceItem mainItem = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_SLICE,
+                null /* hints */, new String[]{android.app.slice.Slice.HINT_LIST_ITEM});
+        mHasHeader = mainItem != null;
 
-        SliceItem title = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE, null);
-        if (title == null) {
+        List<SliceItem> subItems = SliceQuery.findAll(slice,
+                android.app.slice.SliceItem.FORMAT_SLICE,
+                new String[]{android.app.slice.Slice.HINT_LIST_ITEM},
+                null /* nonHints */);
+
+        if (!mHasHeader) {
             mTitle.setVisibility(GONE);
         } else {
             mTitle.setVisibility(VISIBLE);
-            mTitle.setText(title.getText());
+            SliceItem mainTitle = SliceQuery.find(mainItem.getSlice(),
+                    android.app.slice.SliceItem.FORMAT_TEXT,
+                    new String[]{android.app.slice.Slice.HINT_TITLE},
+                    null /* nonHints */);
+            mTitle.setText(mainTitle.getText());
         }
 
-        SliceItem text = SliceQuery.find(mSlice, SliceItem.FORMAT_TEXT, null, Slice.HINT_TITLE);
-        if (text == null) {
-            mText.setVisibility(GONE);
-        } else {
-            mText.setVisibility(VISIBLE);
-            mText.setText(text.getText());
+        mClickActions.clear();
+        final int subItemsCount = subItems.size();
+
+        for (int i = 0; i < subItemsCount; i++) {
+            SliceItem item = subItems.get(i);
+            final Uri itemTag = item.getSlice().getUri();
+            // Try to reuse the view if already exists in the layout
+            KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
+            if (button == null) {
+                button = new KeyguardSliceButton(mContext);
+                button.setTextColor(mTextColor);
+                button.setTag(itemTag);
+            } else {
+                mRow.removeView(button);
+            }
+            button.setHasDivider(i < subItemsCount - 1);
+            mRow.addView(button, i);
+
+            PendingIntent pendingIntent;
+            try {
+                pendingIntent = item.getAction();
+            } catch (RuntimeException e) {
+                Log.w(TAG, "Cannot retrieve action from keyguard slice", e);
+                pendingIntent = null;
+            }
+            mClickActions.put(button, pendingIntent);
+
+            SliceItem title = SliceQuery.find(item.getSlice(),
+                    android.app.slice.SliceItem.FORMAT_TEXT,
+                    new String[]{android.app.slice.Slice.HINT_TITLE},
+                    null /* nonHints */);
+            button.setText(title.getText());
+
+            Drawable iconDrawable = null;
+            SliceItem icon = SliceQuery.find(item.getSlice(),
+                    android.app.slice.SliceItem.FORMAT_IMAGE);
+            if (icon != null) {
+                iconDrawable = icon.getIcon().loadDrawable(mContext);
+                final int width = (int) (iconDrawable.getIntrinsicWidth()
+                        / (float) iconDrawable.getIntrinsicHeight() * mIconSize);
+                iconDrawable.setBounds(0, 0, Math.max(width, 1), mIconSize);
+            }
+            button.setCompoundDrawablesRelative(iconDrawable, null, null, null);
+            button.setOnClickListener(this);
         }
 
-        final int visibility = title == null && text == null ? GONE : VISIBLE;
+        // Removing old views
+        for (int i = 0; i < mRow.getChildCount(); i++) {
+            View child = mRow.getChildAt(i);
+            if (!mClickActions.containsKey(child)) {
+                mRow.removeView(child);
+                i--;
+            }
+        }
+
+        final int visibility = mHasHeader || subItemsCount > 0 ? VISIBLE : GONE;
         if (visibility != getVisibility()) {
             setVisibility(visibility);
         }
+
+        mListener.accept(mHasHeader);
     }
 
     public void setDark(float darkAmount) {
@@ -135,30 +203,113 @@
         updateTextColors();
     }
 
-    public void setTextColor(int textColor) {
-        mTextColor = textColor;
-    }
-
     private void updateTextColors() {
         final int blendedColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
         mTitle.setTextColor(blendedColor);
-        mText.setTextColor(blendedColor);
+        int childCount = mRow.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View v = mRow.getChildAt(i);
+            if (v instanceof Button) {
+                ((Button) v).setTextColor(blendedColor);
+            }
+        }
     }
 
-    private class KeyguardSliceObserver extends ContentObserver {
-        KeyguardSliceObserver(Handler handler) {
-            super(handler);
+    @Override
+    public void onClick(View v) {
+        final PendingIntent action = mClickActions.get(v);
+        if (action != null) {
+            try {
+                action.send();
+            } catch (PendingIntent.CanceledException e) {
+                Log.i(TAG, "Pending intent cancelled, nothing to launch", e);
+            }
+        }
+    }
+
+    public void setListener(Consumer<Boolean> listener) {
+        mListener = listener;
+    }
+
+    public boolean hasHeader() {
+        return mHasHeader;
+    }
+
+    /**
+     * LiveData observer lifecycle.
+     * @param slice the new slice content.
+     */
+    @Override
+    public void onChanged(Slice slice) {
+        showSlice(slice);
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        setupUri(newValue);
+    }
+
+    public void setupUri(String uriString) {
+        if (uriString == null) {
+            uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
+        }
+
+        boolean wasObserving = false;
+        if (mLiveData != null && mLiveData.hasActiveObservers()) {
+            wasObserving = true;
+            mLiveData.removeObserver(this);
+        }
+
+        mKeyguardSliceUri = Uri.parse(uriString);
+        mLiveData = SliceLiveData.fromUri(mContext, mKeyguardSliceUri);
+
+        if (wasObserving) {
+            mLiveData.observeForever(this);
+            showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri));
+        }
+    }
+
+    /**
+     * Representation of an item that appears under the clock on main keyguard message.
+     * Shows optional separator.
+     */
+    private class KeyguardSliceButton extends Button {
+
+        private final Paint mPaint;
+        private boolean mHasDivider;
+
+        public KeyguardSliceButton(Context context) {
+            super(context, null /* attrs */,
+                    com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
+            mPaint = new Paint();
+            mPaint.setStyle(Paint.Style.STROKE);
+            float dividerWidth = context.getResources()
+                    .getDimension(R.dimen.widget_separator_thickness);
+            mPaint.setStrokeWidth(dividerWidth);
+            int horizontalPadding = (int) context.getResources()
+                    .getDimension(R.dimen.widget_horizontal_padding);
+            setPadding(horizontalPadding, 0, horizontalPadding, 0);
+            setCompoundDrawablePadding((int) context.getResources()
+                    .getDimension(R.dimen.widget_icon_padding));
+        }
+
+        public void setHasDivider(boolean hasDivider) {
+            mHasDivider = hasDivider;
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            this.onChange(selfChange, null);
+        public void setTextColor(int color) {
+            super.setTextColor(color);
+            mPaint.setColor(color);
         }
 
         @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
-                    Collections.emptyList()));
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            if (mHasDivider) {
+                final int lineX = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : getWidth();
+                canvas.drawLine(lineX, 0, lineX, getHeight(), mPaint);
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 78cf2b9..4abf886 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -28,6 +28,7 @@
 import android.support.v4.graphics.ColorUtils;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -38,11 +39,11 @@
 import android.widget.TextClock;
 import android.widget.TextView;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.settingslib.Utils;
 import com.android.systemui.ChargingView;
 
+import com.google.android.collect.Sets;
+
 import java.util.Locale;
 
 public class KeyguardStatusView extends GridLayout {
@@ -52,8 +53,11 @@
 
     private final LockPatternUtils mLockPatternUtils;
     private final AlarmManager mAlarmManager;
+    private final float mSmallClockScale;
+    private final float mWidgetPadding;
 
     private TextClock mClockView;
+    private View mClockSeparator;
     private TextView mOwnerInfo;
     private ViewGroup mClockContainer;
     private ChargingView mBatteryDoze;
@@ -61,7 +65,7 @@
     private Runnable mPendingMarqueeStart;
     private Handler mHandler;
 
-    private View[] mVisibleInDoze;
+    private ArraySet<View> mVisibleInDoze;
     private boolean mPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
@@ -112,6 +116,9 @@
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mLockPatternUtils = new LockPatternUtils(getContext());
         mHandler = new Handler(Looper.myLooper());
+        mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
+                / getResources().getDimension(R.dimen.widget_big_font_size);
+        mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
     }
 
     private void setEnableMarquee(boolean enabled) {
@@ -150,9 +157,14 @@
         mOwnerInfo = findViewById(R.id.owner_info);
         mBatteryDoze = findViewById(R.id.battery_doze);
         mKeyguardSlice = findViewById(R.id.keyguard_status_area);
-        mVisibleInDoze = new View[]{mBatteryDoze, mClockView, mKeyguardSlice};
+        mClockSeparator = findViewById(R.id.clock_separator);
+        mVisibleInDoze = Sets.newArraySet(mBatteryDoze, mClockView, mKeyguardSlice,
+                mClockSeparator);
         mTextColor = mClockView.getCurrentTextColor();
 
+        mKeyguardSlice.setListener(this::onSliceContentChanged);
+        onSliceContentChanged(mKeyguardSlice.hasHeader());
+
         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
         setEnableMarquee(shouldMarquee);
         refresh();
@@ -163,6 +175,22 @@
         mClockView.setElegantTextHeight(false);
     }
 
+    private void onSliceContentChanged(boolean hasHeader) {
+        final float clockScale = hasHeader ? mSmallClockScale : 1;
+        float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f;
+        if (hasHeader) {
+            translation -= mWidgetPadding;
+        }
+        mClockView.setTranslationY(translation);
+        mClockView.setScaleX(clockScale);
+        mClockView.setScaleY(clockScale);
+        final float batteryTranslation =
+                -(mClockView.getWidth() - (mClockView.getWidth() * clockScale)) / 2;
+        mBatteryDoze.setTranslationX(batteryTranslation);
+        mBatteryDoze.setTranslationY(translation);
+        mClockSeparator.setVisibility(hasHeader ? VISIBLE : GONE);
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -303,7 +331,7 @@
         final int N = mClockContainer.getChildCount();
         for (int i = 0; i < N; i++) {
             View child = mClockContainer.getChildAt(i);
-            if (ArrayUtils.contains(mVisibleInDoze, child)) {
+            if (mVisibleInDoze.contains(child)) {
                 continue;
             }
             child.setAlpha(dark ? 0 : 1);
@@ -312,10 +340,12 @@
             mOwnerInfo.setAlpha(dark ? 0 : 1);
         }
 
+        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
         updateDozeVisibleViews();
         mBatteryDoze.setDark(dark);
         mKeyguardSlice.setDark(darkAmount);
-        mClockView.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount));
+        mClockView.setTextColor(blendedTextColor);
+        mClockSeparator.setBackgroundColor(blendedTextColor);
     }
 
     public void setPulsing(boolean pulsing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6ddc76b..2b09903 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -24,8 +24,6 @@
 import android.icu.text.DisplayContext;
 import android.net.Uri;
 import android.os.Handler;
-import android.app.slice.Slice;
-import android.app.slice.SliceProvider;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
@@ -33,15 +31,22 @@
 import java.util.Date;
 import java.util.Locale;
 
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
+import androidx.app.slice.builders.ListBuilder;
+import androidx.app.slice.builders.ListBuilder.RowBuilder;
+
 /**
  * Simple Slice provider that shows the current date.
  */
 public class KeyguardSliceProvider extends SliceProvider {
 
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
+    public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
 
     private final Date mCurrentTime = new Date();
     protected final Uri mSliceUri;
+    protected final Uri mDateUri;
     private final Handler mHandler;
     private String mDatePattern;
     private DateFormat mDateFormat;
@@ -80,23 +85,31 @@
     KeyguardSliceProvider(Handler handler) {
         mHandler = handler;
         mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
+        mDateUri = Uri.parse(KEYGUARD_DATE_URI);
     }
 
+
+
     @Override
     public Slice onBindSlice(Uri sliceUri) {
-        return new Slice.Builder(sliceUri).addText(mLastText, null, Slice.HINT_TITLE).build();
+        return new ListBuilder(mSliceUri)
+                .addRow(new RowBuilder(mDateUri).setTitle(mLastText)).build();
     }
 
     @Override
-    public boolean onCreate() {
-
+    public boolean onCreateSliceProvider() {
         mDatePattern = getContext().getString(R.string.system_ui_date_pattern);
-
         registerClockUpdate(false /* everyMinute */);
         updateClock();
         return true;
     }
 
+    /**
+     * Registers a broadcast receiver for clock updates, include date, time zone and manually
+     * changing the date/time via the settings app.
+     *
+     * @param everyMinute {@code true} if you also want updates every minute.
+     */
     protected void registerClockUpdate(boolean everyMinute) {
         if (mRegistered) {
             if (mRegisteredEveryMinute == everyMinute) {
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 9d44895..066cfe5 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -46,7 +46,12 @@
     android-support-v7-mediarouter \
     android-support-v7-palette \
     android-support-v14-preference \
-    android-support-v17-leanback
+    android-support-v17-leanback \
+    android-slices-core \
+    android-slices-view \
+    android-slices-builders \
+    apptoolkit-arch-core-runtime \
+    apptoolkit-lifecycle-extensions \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     metrics-helper-lib \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 4eae342..be28569 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -16,11 +16,10 @@
 
 package com.android.systemui.keyguard;
 
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
+import androidx.app.slice.Slice;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Debug;
 import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -34,6 +33,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -63,7 +65,8 @@
     @Test
     public void returnsValidSlice() {
         Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI));
-        SliceItem text = SliceQuery.find(slice, SliceItem.FORMAT_TEXT, Slice.HINT_TITLE,
+        SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT,
+                android.app.slice.Slice.HINT_TITLE,
                 null /* nonHints */);
         Assert.assertNotNull("Slice must provide a title.", text);
     }
@@ -78,9 +81,10 @@
 
     @Test
     public void updatesClock() {
+        mProvider.mUpdateClockInvokations = 0;
         mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK));
         TestableLooper.get(this).processAllMessages();
-        Assert.assertEquals("Clock should have been updated.", 2 /* expected */,
+        Assert.assertEquals("Clock should have been updated.", 1 /* expected */,
                 mProvider.mUpdateClockInvokations);
     }