diff --git a/packages/SystemUI/res-keyguard/layout/type_clock.xml b/packages/SystemUI/res-keyguard/layout/type_clock.xml
new file mode 100644
index 0000000000000..21c64e9c7dbe2
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/type_clock.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml
index 7a849ebd481d0..74ee7ffad3f6c 100644
--- a/packages/SystemUI/res-keyguard/values/colors.xml
+++ b/packages/SystemUI/res-keyguard/values/colors.xml
@@ -19,4 +19,6 @@
#C97343
#F5C983
+
+ #F5C983
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 1d5aa6d769911..7432f9cde639a 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -402,4 +402,87 @@ number">%d remaining attempt before SIM becomes permanently unusable.
number">%d remaining attempts before SIM becomes permanently unusable. Contact carrier for details.
+
+ It\u2019s
+
+
+
+ - Twelve
+ - One
+ - Two
+ - Three
+ - Four
+ - Five
+ - Six
+ - Seven
+ - Eight
+ - Nine
+ - Ten
+ - Eleven
+
+
+
+
+ - O\u2019Clock
+ - O\u2019One
+ - O\u2019Two
+ - O\u2019Three
+ - O\u2019Four
+ - O\u2019Five
+ - O\u2019Six
+ - O\u2019Seven
+ - O\u2019Eight
+ - O\u2019Nine
+ - Ten
+ - Eleven
+ - Twelve
+ - Thirteen
+ - Fourteen
+ - Fifteen
+ - Sixteen
+ - Seventeen
+ - Eighteen
+ - Nineteen
+ - Twenty
+ - Twenty\nOne
+ - Twenty\nTwo
+ - Twenty\nThree
+ - Twenty\nFour
+ - Twenty\nFive
+ - Twenty\nSix
+ - Twenty\nSeven
+ - Twenty\nEight
+ - Twenty\nNine
+ - Thirty
+ - Thirty\nOne
+ - Thirty\nTwo
+ - Thirty\nThree
+ - Thirty\nFour
+ - Thirty\nFive
+ - Thirty\nSix
+ - Thirty\nSeven
+ - Thirty\nEight
+ - Thirty\nNine
+ - Forty
+ - Forty\nOne
+ - Forty\nTwo
+ - Forty\nThree
+ - Forty\nFour
+ - Forty\nFive
+ - Forty\nSix
+ - Forty\nSeven
+ - Forty\nEight
+ - Forty\nNine
+ - Fifty
+ - Fifty\nOne
+ - Fifty\nTwo
+ - Fifty\nThree
+ - Fifty\nFour
+ - Fifty\nFive
+ - Fifty\nSix
+ - Fifty\nSeven
+ - Fifty\nEight
+ - Fifty\nNine
+
+
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a8094d20d5a21..198ff4a0153bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.clock.BubbleClockController;
import com.android.keyguard.clock.StretchAnalogClockController;
+import com.android.keyguard.clock.TypeClockController;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
@@ -153,6 +154,12 @@ public class KeyguardClockSwitch extends RelativeLayout {
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
StretchAnalogClockController.class.getName(),
() -> StretchAnalogClockController.build(mLayoutInflater)))
+ .withDefault(
+ new SettingsGattedSupplier(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+ TypeClockController.class.getName(),
+ () -> TypeClockController.build(mLayoutInflater)))
.build();
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
index 5aa566848732c..3591dc82c8ec2 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -36,6 +36,7 @@ public class ClockLayout extends FrameLayout {
*/
private View mDigitalClock;
private View mAnalogClock;
+ private View mTypeClock;
/**
* Pixel shifting amplitidues used to prevent screen burn-in.
@@ -60,6 +61,7 @@ public class ClockLayout extends FrameLayout {
super.onFinishInflate();
mDigitalClock = findViewById(R.id.digital_clock);
mAnalogClock = findViewById(R.id.analog_clock);
+ mTypeClock = findViewById(R.id.type_clock);
// Get pixel shifting X, Y amplitudes from resources.
Resources resources = getResources();
@@ -89,5 +91,11 @@ public class ClockLayout extends FrameLayout {
mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
+ offsetY);
}
+
+ // Put the typographic clock part way down the screen.
+ if (mTypeClock != null) {
+ mTypeClock.setX(offsetX);
+ mTypeClock.setY(0.2f * getHeight() + offsetY);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
new file mode 100644
index 0000000000000..17d929dc8a3b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Plugin for a custom Typographic clock face that displays the time in words.
+ */
+public class TypeClockController implements ClockPlugin {
+
+ /**
+ * Custom clock shown on AOD screen and behind stack scroller on lock.
+ */
+ private View mView;
+ private TypographicClock mTypeClock;
+
+ /**
+ * Small clock shown on lock screen above stack scroller.
+ */
+ private View mLockClockContainer;
+
+ /**
+ * Controller for transition into dark state.
+ */
+ private CrossFadeDarkController mDarkController;
+
+ private TypeClockController() {}
+
+ /**
+ * Create a TypeClockController instance.
+ *
+ * @param inflater Inflater used to inflate custom clock views.
+ */
+ public static TypeClockController build(LayoutInflater inflater) {
+ TypeClockController controller = new TypeClockController();
+ controller.createViews(inflater);
+ return controller;
+ }
+
+ private void createViews(LayoutInflater inflater) {
+ mView = inflater.inflate(R.layout.type_clock, null);
+ mTypeClock = mView.findViewById(R.id.type_clock);
+
+ // For now, this view is used to hide the default digital clock.
+ // Need better transition to lock screen.
+ mLockClockContainer = inflater.inflate(R.layout.digital_clock, null);
+ mLockClockContainer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public View getView() {
+ return mLockClockContainer;
+ }
+
+ @Override
+ public View getBigClockView() {
+ return mView;
+ }
+
+ @Override
+ public void setStyle(Style style) {}
+
+ @Override
+ public void setTextColor(int color) {
+ mTypeClock.setTextColor(color);
+ }
+
+ @Override
+ public void dozeTimeTick() {
+ mTypeClock.onTimeChanged();
+ }
+
+ @Override
+ public void setDarkAmount(float darkAmount) {}
+
+ @Override
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ mTypeClock.onTimeZoneChanged(timeZone);
+ }
+
+ @Override
+ public boolean shouldShowStatusArea() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
new file mode 100644
index 0000000000000..5f9da3ee33bb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.keyguard.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Clock that presents the time in words.
+ */
+public class TypographicClock extends LinearLayout {
+
+ private final String[] mHours;
+ private final String[] mMinutes;
+ private TextView mHeaderText;
+ private TextView mHourText;
+ private TextView mMinuteText;
+ private Calendar mTime;
+ private String mDescFormat;
+ private TimeZone mTimeZone;
+
+ public TypographicClock(Context context) {
+ this(context, null);
+ }
+
+ public TypographicClock(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mTime = Calendar.getInstance();
+ mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
+ Resources res = context.getResources();
+ mHours = res.getStringArray(R.array.type_clock_hours);
+ mMinutes = res.getStringArray(R.array.type_clock_minutes);
+ }
+
+ /**
+ * Call when the time changes to update the text of the time.
+ */
+ public void onTimeChanged() {
+ mTime.setTimeInMillis(System.currentTimeMillis());
+ setContentDescription(DateFormat.format(mDescFormat, mTime));
+ final int hour = mTime.get(Calendar.HOUR);
+ mHourText.setText(mHours[hour % 12]);
+ final int minute = mTime.get(Calendar.MINUTE);
+ mMinuteText.setText(mMinutes[minute % 60]);
+ invalidate();
+ }
+
+ /**
+ * Call when the time zone has changed to update clock time.
+ *
+ * @param timeZone The updated time zone that will be used.
+ */
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ mTimeZone = timeZone;
+ mTime.setTimeZone(timeZone);
+ }
+
+ /**
+ * Set the color of the text used to display the time.
+ *
+ * This is necessary when the wallpaper shown behind the clock on the
+ * lock screen changes.
+ */
+ public void setTextColor(int color) {
+ mHourText.setTextColor(color);
+ mMinuteText.setTextColor(color);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mHeaderText = findViewById(R.id.header);
+ mHourText = findViewById(R.id.hour);
+ mMinuteText = findViewById(R.id.minute);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+ onTimeChanged();
+ }
+}