Merge changes I28a7b836,I6626351d,Ie4490fb8
* changes:
AOSP/DeskClock - Add Kotlin files for AutoSizing and CircleView widgets
AOSP/DeskClock - Add Kotlin files for widget/toast directory
AOSP/DeskClock - Add Kotlin files for AlarmSelection and Adapter
diff --git a/Android.bp b/Android.bp
index 5bcfd5c..552fc13 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,6 +57,11 @@
"src/**/deskclock/stopwatch/*.java",
"src/**/deskclock/timer/*.java",
"src/**/deskclock/uidata/*.java",
+ "src/**/deskclock/widget/selector/*.java",
+ "src/**/deskclock/widget/toast/*.java",
+ "src/**/deskclock/widget/AutoSizingTextClock.java",
+ "src/**/deskclock/widget/AutoSizingTextView.java",
+ "src/**/deskclock/widget/CircleView.java",
],
product_specific: true,
static_libs: [
diff --git a/src/com/android/deskclock/widget/AutoSizingTextClock.kt b/src/com/android/deskclock/widget/AutoSizingTextClock.kt
new file mode 100644
index 0000000..c332762
--- /dev/null
+++ b/src/com/android/deskclock/widget/AutoSizingTextClock.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.TextClock
+
+/**
+ * Wrapper around TextClock that automatically re-sizes itself to fit within the given bounds.
+ */
+class AutoSizingTextClock @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : TextClock(context, attrs, defStyleAttr) {
+ private val mTextSizeHelper: TextSizeHelper? = TextSizeHelper(this)
+
+ private var mSuppressLayout = false
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ mTextSizeHelper!!.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+
+ override fun onTextChanged(
+ text: CharSequence,
+ start: Int,
+ lengthBefore: Int,
+ lengthAfter: Int
+ ) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter)
+ if (mTextSizeHelper != null) {
+ if (lengthBefore != lengthAfter) {
+ mSuppressLayout = false
+ }
+ mTextSizeHelper.onTextChanged(lengthBefore, lengthAfter)
+ } else {
+ requestLayout()
+ }
+ }
+
+ override fun setText(text: CharSequence, type: BufferType) {
+ mSuppressLayout = true
+ super.setText(text, type)
+ mSuppressLayout = false
+ }
+
+ override fun requestLayout() {
+ if (mTextSizeHelper == null || !mTextSizeHelper.shouldIgnoreRequestLayout()) {
+ if (!mSuppressLayout) {
+ super.requestLayout()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/AutoSizingTextView.kt b/src/com/android/deskclock/widget/AutoSizingTextView.kt
new file mode 100644
index 0000000..d393542
--- /dev/null
+++ b/src/com/android/deskclock/widget/AutoSizingTextView.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.appcompat.widget.AppCompatTextView
+
+/**
+ * A TextView which automatically re-sizes its text to fit within its boundaries.
+ */
+class AutoSizingTextView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = android.R.attr.textViewStyle
+) : AppCompatTextView(context, attrs, defStyleAttr) {
+ private val mTextSizeHelper: TextSizeHelper? = TextSizeHelper(this)
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ mTextSizeHelper!!.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+
+ override fun onTextChanged(
+ text: CharSequence?,
+ start: Int,
+ lengthBefore: Int,
+ lengthAfter: Int
+ ) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter)
+ if (mTextSizeHelper != null) {
+ mTextSizeHelper.onTextChanged(lengthBefore, lengthAfter)
+ } else {
+ requestLayout()
+ }
+ }
+
+ override fun requestLayout() {
+ if (mTextSizeHelper == null || !mTextSizeHelper.shouldIgnoreRequestLayout()) {
+ super.requestLayout()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/CircleView.kt b/src/com/android/deskclock/widget/CircleView.kt
new file mode 100644
index 0000000..062a284
--- /dev/null
+++ b/src/com/android/deskclock/widget/CircleView.kt
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.util.Property
+import android.view.Gravity
+import android.view.View
+
+import com.android.deskclock.R
+
+import kotlin.math.min
+
+/**
+ * A [View] that draws primitive circles.
+ */
+class CircleView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+ /** The [Paint] used to draw the circle. */
+ private val mCirclePaint = Paint()
+
+ /** the current [Gravity] used to align/size the circle */
+ var gravity: Int
+ private set
+
+ private var mCenterX: Float
+ private var mCenterY: Float
+
+ /** the radius of the circle */
+ var radius: Float
+ private set
+
+ init {
+ val a = context.obtainStyledAttributes(attrs, R.styleable.CircleView, defStyleAttr, 0)
+
+ gravity = a.getInt(R.styleable.CircleView_android_gravity, Gravity.NO_GRAVITY)
+ mCenterX = a.getDimension(R.styleable.CircleView_centerX, 0.0f)
+ mCenterY = a.getDimension(R.styleable.CircleView_centerY, 0.0f)
+ radius = a.getDimension(R.styleable.CircleView_radius, 0.0f)
+
+ mCirclePaint.color = a.getColor(R.styleable.CircleView_fillColor, Color.WHITE)
+
+ a.recycle()
+ }
+
+ override fun onRtlPropertiesChanged(layoutDirection: Int) {
+ super.onRtlPropertiesChanged(layoutDirection)
+
+ if (gravity != Gravity.NO_GRAVITY) {
+ applyGravity(gravity, layoutDirection)
+ }
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+
+ if (gravity != Gravity.NO_GRAVITY) {
+ applyGravity(gravity, layoutDirection)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ // draw the circle, duh
+ canvas.drawCircle(mCenterX, mCenterY, radius, mCirclePaint)
+ }
+
+ override fun hasOverlappingRendering(): Boolean {
+ // only if we have a background, which we shouldn't...
+ return background != null
+ }
+
+ /**
+ * Describes how to align/size the circle relative to the view's bounds. Defaults to
+ * [Gravity.NO_GRAVITY].
+ *
+ * Note: using [.setCenterX], [.setCenterY], or
+ * [.setRadius] will automatically clear any conflicting gravity bits.
+ *
+ * @param gravity the [Gravity] flags to use
+ * @return this object, allowing calls to methods in this class to be chained
+ * @see R.styleable.CircleView_android_gravity
+ */
+ fun setGravity(gravity: Int): CircleView {
+ if (this.gravity != gravity) {
+ this.gravity = gravity
+
+ if (gravity != Gravity.NO_GRAVITY && isLayoutDirectionResolved) {
+ applyGravity(gravity, layoutDirection)
+ }
+ }
+ return this
+ }
+
+ /**
+ * @return the ARGB color used to fill the circle
+ */
+ val fillColor: Int
+ get() = mCirclePaint.color
+
+ /**
+ * Sets the ARGB color used to fill the circle and invalidates only the affected area.
+ *
+ * @param color the ARGB color to use
+ * @return this object, allowing calls to methods in this class to be chained
+ * @see R.styleable.CircleView_fillColor
+ */
+ fun setFillColor(color: Int): CircleView {
+ if (mCirclePaint.color != color) {
+ mCirclePaint.color = color
+
+ // invalidate the current area
+ invalidate(mCenterX, mCenterY, radius)
+ }
+ return this
+ }
+
+ /**
+ * Sets the x-coordinate for the center of the circle and invalidates only the affected area.
+ *
+ * @param centerX the x-coordinate to use, relative to the view's bounds
+ * @return this object, allowing calls to methods in this class to be chained
+ * @see R.styleable.CircleView_centerX
+ */
+ fun setCenterX(centerX: Float): CircleView {
+ val oldCenterX = mCenterX
+ if (oldCenterX != centerX) {
+ mCenterX = centerX
+
+ // invalidate the old/new areas
+ invalidate(oldCenterX, mCenterY, radius)
+ invalidate(centerX, mCenterY, radius)
+ }
+
+ // clear the horizontal gravity flags
+ gravity = gravity and Gravity.HORIZONTAL_GRAVITY_MASK.inv()
+
+ return this
+ }
+
+ /**
+ * Sets the y-coordinate for the center of the circle and invalidates only the affected area.
+ *
+ * @param centerY the y-coordinate to use, relative to the view's bounds
+ * @return this object, allowing calls to methods in this class to be chained
+ * @see R.styleable.CircleView_centerY
+ */
+ fun setCenterY(centerY: Float): CircleView {
+ val oldCenterY = mCenterY
+ if (oldCenterY != centerY) {
+ mCenterY = centerY
+
+ // invalidate the old/new areas
+ invalidate(mCenterX, oldCenterY, radius)
+ invalidate(mCenterX, centerY, radius)
+ }
+
+ // clear the vertical gravity flags
+ gravity = gravity and Gravity.VERTICAL_GRAVITY_MASK.inv()
+
+ return this
+ }
+
+ /**
+ * Sets the radius of the circle and invalidates only the affected area.
+ *
+ * @param radius the radius to use
+ * @return this object, allowing calls to methods in this class to be chained
+ * @see R.styleable.CircleView_radius
+ */
+ fun setRadius(radius: Float): CircleView {
+ val oldRadius = this.radius
+ if (oldRadius != radius) {
+ this.radius = radius
+
+ // invalidate the old/new areas
+ invalidate(mCenterX, mCenterY, oldRadius)
+ if (radius > oldRadius) {
+ invalidate(mCenterX, mCenterY, radius)
+ }
+ }
+
+ // clear the fill gravity flags
+ if (gravity and Gravity.FILL_HORIZONTAL == Gravity.FILL_HORIZONTAL) {
+ gravity = gravity and Gravity.FILL_HORIZONTAL.inv()
+ }
+ if (gravity and Gravity.FILL_VERTICAL == Gravity.FILL_VERTICAL) {
+ gravity = gravity and Gravity.FILL_VERTICAL.inv()
+ }
+
+ return this
+ }
+
+ /**
+ * Invalidates the rectangular area that circumscribes the circle defined by `centerX`,
+ * `centerY`, and `radius`.
+ */
+ private fun invalidate(centerX: Float, centerY: Float, radius: Float) {
+ invalidate((centerX - radius - 0.5f).toInt(), (centerY - radius - 0.5f).toInt(),
+ (centerX + radius + 0.5f).toInt(), (centerY + radius + 0.5f).toInt())
+ }
+
+ /**
+ * Applies the specified `gravity` and `layoutDirection`, adjusting the alignment
+ * and size of the circle depending on the resolved [Gravity] flags. Also invalidates the
+ * affected area if necessary.
+ *
+ * @param gravity the [Gravity] the [Gravity] flags to use
+ * @param layoutDirection the layout direction used to resolve the absolute gravity
+ */
+ @SuppressLint("RtlHardcoded")
+ private fun applyGravity(gravity: Int, layoutDirection: Int) {
+ val absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection)
+
+ val oldRadius = radius
+ val oldCenterX = mCenterX
+ val oldCenterY = mCenterY
+
+ when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
+ Gravity.LEFT -> mCenterX = 0.0f
+ Gravity.CENTER_HORIZONTAL, Gravity.FILL_HORIZONTAL -> mCenterX = width / 2.0f
+ Gravity.RIGHT -> mCenterX = width.toFloat()
+ }
+
+ when (absoluteGravity and Gravity.VERTICAL_GRAVITY_MASK) {
+ Gravity.TOP -> mCenterY = 0.0f
+ Gravity.CENTER_VERTICAL, Gravity.FILL_VERTICAL -> mCenterY = height / 2.0f
+ Gravity.BOTTOM -> mCenterY = height.toFloat()
+ }
+
+ when (absoluteGravity and Gravity.FILL) {
+ Gravity.FILL -> radius = min(width, height) / 2.0f
+ Gravity.FILL_HORIZONTAL -> radius = width / 2.0f
+ Gravity.FILL_VERTICAL -> radius = height / 2.0f
+ }
+
+ if (oldCenterX != mCenterX || oldCenterY != mCenterY || oldRadius != radius) {
+ invalidate(oldCenterX, oldCenterY, oldRadius)
+ invalidate(mCenterX, mCenterY, radius)
+ }
+ }
+
+ companion object {
+ /**
+ * A Property wrapper around the fillColor functionality handled by the
+ * [.setFillColor] and [.getFillColor] methods.
+ */
+ @JvmField
+ val FILL_COLOR: Property<CircleView, Int> =
+ object : Property<CircleView, Int>(Int::class.java, "fillColor") {
+ override fun get(view: CircleView): Int {
+ return view.fillColor
+ }
+
+ override fun set(view: CircleView, value: Int) {
+ view.setFillColor(value)
+ }
+ }
+
+ /**
+ * A Property wrapper around the radius functionality handled by the
+ * [.setRadius] and [.getRadius] methods.
+ */
+ @JvmField
+ val RADIUS: Property<CircleView, Float> =
+ object : Property<CircleView, Float>(Float::class.java, "radius") {
+ override fun get(view: CircleView): Float {
+ return view.radius
+ }
+
+ override fun set(view: CircleView, value: Float) {
+ view.setRadius(value)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/selector/AlarmSelection.kt b/src/com/android/deskclock/widget/selector/AlarmSelection.kt
new file mode 100644
index 0000000..bee3285
--- /dev/null
+++ b/src/com/android/deskclock/widget/selector/AlarmSelection.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget.selector
+
+import com.android.deskclock.provider.Alarm
+
+/**
+ * Created a new selectable item with a visual label and an id.
+ * id corresponds to the Alarm id
+ */
+class AlarmSelection(val label: String, val alarm: Alarm)
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/selector/AlarmSelectionAdapter.kt b/src/com/android/deskclock/widget/selector/AlarmSelectionAdapter.kt
new file mode 100644
index 0000000..8858d64
--- /dev/null
+++ b/src/com/android/deskclock/widget/selector/AlarmSelectionAdapter.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget.selector
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.TextView
+
+import com.android.deskclock.R
+import com.android.deskclock.data.DataModel
+import com.android.deskclock.data.Weekdays
+import com.android.deskclock.provider.Alarm
+import com.android.deskclock.widget.TextTime
+
+import java.util.Calendar
+
+class AlarmSelectionAdapter(
+ context: Context,
+ id: Int,
+ alarms: List<AlarmSelection>
+) : ArrayAdapter<AlarmSelection?>(context, id, alarms) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val context = context
+ var row = convertView
+ if (row == null) {
+ val inflater = LayoutInflater.from(context)
+ row = inflater.inflate(R.layout.alarm_row, parent, false)
+ }
+
+ val selection = getItem(position)
+ val alarm = selection?.alarm
+
+ val alarmTime = row!!.findViewById<View>(R.id.digital_clock) as TextTime
+ alarmTime.setTime(alarm!!.hour, alarm.minutes)
+
+ val alarmLabel = row.findViewById<View>(R.id.label) as TextView
+ alarmLabel.text = alarm.label
+
+ // find days when alarm is firing
+ val daysOfWeek: String
+ daysOfWeek = if (!alarm.daysOfWeek.isRepeating) {
+ if (Alarm.isTomorrow(alarm, Calendar.getInstance())) {
+ context.resources.getString(R.string.alarm_tomorrow)
+ } else {
+ context.resources.getString(R.string.alarm_today)
+ }
+ } else {
+ val weekdayOrder: Weekdays.Order = DataModel.dataModel.weekdayOrder
+ alarm.daysOfWeek.toString(context, weekdayOrder)
+ }
+
+ val daysOfWeekView = row.findViewById<View>(R.id.daysOfWeek) as TextView
+ daysOfWeekView.text = daysOfWeek
+
+ return row
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/toast/SnackbarManager.kt b/src/com/android/deskclock/widget/toast/SnackbarManager.kt
new file mode 100644
index 0000000..0d242f2
--- /dev/null
+++ b/src/com/android/deskclock/widget/toast/SnackbarManager.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget.toast
+
+import com.google.android.material.snackbar.Snackbar
+
+import java.lang.ref.WeakReference
+
+/**
+ * Manages visibility of Snackbar and allow preemptive dismiss of current displayed Snackbar.
+ */
+object SnackbarManager {
+ private var sSnackbar: WeakReference<Snackbar>? = null
+
+ @JvmStatic
+ fun show(snackbar: Snackbar) {
+ sSnackbar = WeakReference<Snackbar>(snackbar)
+ snackbar.show()
+ }
+
+ @JvmStatic
+ fun dismiss() {
+ val snackbar: Snackbar? = sSnackbar?.get()
+ if (snackbar != null) {
+ snackbar.dismiss()
+ sSnackbar = null
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/toast/SnackbarSlidingBehavior.kt b/src/com/android/deskclock/widget/toast/SnackbarSlidingBehavior.kt
new file mode 100644
index 0000000..4ebfc7c
--- /dev/null
+++ b/src/com/android/deskclock/widget/toast/SnackbarSlidingBehavior.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget.toast
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import androidx.annotation.Keep
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+
+import com.google.android.material.snackbar.Snackbar
+
+import kotlin.math.min
+
+/**
+ * Custom [CoordinatorLayout.Behavior] that slides with the [Snackbar].
+ */
+@Keep
+class SnackbarSlidingBehavior(
+ context: Context?,
+ attrs: AttributeSet?
+) : CoordinatorLayout.Behavior<View?>() {
+ override fun layoutDependsOn(
+ parent: CoordinatorLayout,
+ child: View,
+ dependency: View
+ ): Boolean {
+ return dependency is Snackbar.SnackbarLayout
+ }
+
+ override fun onDependentViewChanged(
+ parent: CoordinatorLayout,
+ child: View,
+ dependency: View
+ ): Boolean {
+ updateTranslationY(parent, child)
+ return false
+ }
+
+ override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
+ updateTranslationY(parent, child)
+ }
+
+ override fun onLayoutChild(
+ parent: CoordinatorLayout,
+ child: View,
+ layoutDirection: Int
+ ): Boolean {
+ updateTranslationY(parent, child)
+ return false
+ }
+
+ private fun updateTranslationY(parent: CoordinatorLayout, child: View) {
+ var translationY = 0f
+ for (dependency in parent.getDependencies(child)) {
+ translationY = min(translationY, dependency.y - child.bottom)
+ }
+ child.translationY = translationY
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/widget/toast/ToastManager.kt b/src/com/android/deskclock/widget/toast/ToastManager.kt
new file mode 100644
index 0000000..49ef9b7
--- /dev/null
+++ b/src/com/android/deskclock/widget/toast/ToastManager.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.deskclock.widget.toast
+
+import android.widget.Toast
+
+object ToastManager {
+ private var sToast: Toast? = null
+
+ @JvmStatic
+ fun setToast(toast: Toast) {
+ sToast?.cancel()
+ sToast = toast
+ }
+
+ @JvmStatic
+ fun cancelToast() {
+ sToast?.cancel()
+ sToast = null
+ }
+}
\ No newline at end of file