summaryrefslogtreecommitdiff
path: root/src/com/android/customization/picker/clock/ui/view
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/customization/picker/clock/ui/view')
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt86
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockSectionView.kt23
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt50
-rw-r--r--src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt75
-rw-r--r--src/com/android/customization/picker/clock/ui/view/SaturationProgressDrawable.kt107
5 files changed, 341 insertions, 0 deletions
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
new file mode 100644
index 00000000..90d7c42d
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.constraintlayout.helper.widget.Carousel
+import androidx.core.view.get
+import com.android.wallpaper.R
+
+class ClockCarouselView(
+ context: Context,
+ attrs: AttributeSet,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ ) {
+
+ private val carousel: Carousel
+ private lateinit var adapter: ClockCarouselAdapter
+
+ init {
+ val view = LayoutInflater.from(context).inflate(R.layout.clock_carousel, this)
+ carousel = view.requireViewById(R.id.carousel)
+ }
+
+ fun setUpClockCarouselView(
+ clockIds: List<String>,
+ onGetClockPreview: (clockId: String) -> View,
+ onClockSelected: (clockId: String) -> Unit,
+ ) {
+ adapter = ClockCarouselAdapter(clockIds, onGetClockPreview, onClockSelected)
+ carousel.setAdapter(adapter)
+ carousel.refresh()
+ }
+
+ fun setSelectedClockIndex(
+ index: Int,
+ ) {
+ carousel.jumpToIndex(index)
+ }
+
+ class ClockCarouselAdapter(
+ val clockIds: List<String>,
+ val onGetClockPreview: (clockId: String) -> View,
+ val onClockSelected: (clockId: String) -> Unit,
+ ) : Carousel.Adapter {
+
+ override fun count(): Int {
+ return clockIds.size
+ }
+
+ override fun populate(view: View?, index: Int) {
+ val viewRoot = view as ViewGroup
+ val clockHostView = viewRoot[0] as ViewGroup
+ clockHostView.removeAllViews()
+ val clockView = onGetClockPreview(clockIds[index])
+ // The clock view might still be attached to an existing parent. Detach before adding to
+ // another parent.
+ (clockView.parent as? ViewGroup)?.removeView(clockView)
+ clockHostView.addView(clockView)
+ }
+
+ override fun onNewItem(index: Int) {
+ onClockSelected.invoke(clockIds[index])
+ }
+ }
+}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockSectionView.kt b/src/com/android/customization/picker/clock/ui/view/ClockSectionView.kt
new file mode 100644
index 00000000..cca107c4
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockSectionView.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.customization.picker.clock.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.wallpaper.picker.SectionView
+
+/** The [SectionView] for app clock. */
+class ClockSectionView(context: Context?, attrs: AttributeSet?) : SectionView(context, attrs)
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt b/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt
new file mode 100644
index 00000000..909491a3
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockSizeRadioButtonGroup.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.RadioButton
+import com.android.customization.picker.clock.shared.ClockSize
+import com.android.wallpaper.R
+
+/** The radio button group to pick the clock size. */
+class ClockSizeRadioButtonGroup(
+ context: Context,
+ attrs: AttributeSet?,
+) : FrameLayout(context, attrs) {
+
+ interface OnRadioButtonClickListener {
+ fun onClick(size: ClockSize)
+ }
+
+ val radioButtonDynamic: RadioButton
+ val radioButtonSmall: RadioButton
+ var onRadioButtonClickListener: OnRadioButtonClickListener? = null
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.clock_size_radio_button_group, this, true)
+ radioButtonDynamic = requireViewById(R.id.radio_button_dynamic)
+ val buttonDynamic = requireViewById<View>(R.id.button_container_dynamic)
+ buttonDynamic.setOnClickListener { onRadioButtonClickListener?.onClick(ClockSize.DYNAMIC) }
+ radioButtonSmall = requireViewById(R.id.radio_button_large)
+ val buttonLarge = requireViewById<View>(R.id.button_container_small)
+ buttonLarge.setOnClickListener { onRadioButtonClickListener?.onClick(ClockSize.SMALL) }
+ }
+}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
new file mode 100644
index 00000000..7f480dee
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.view
+
+import android.app.Activity
+import android.view.View
+import androidx.annotation.ColorInt
+import com.android.systemui.plugins.ClockController
+import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.wallpaper.R
+import com.android.wallpaper.util.ScreenSizeCalculator
+import com.android.wallpaper.util.TimeUtils.TimeTicker
+
+class ClockViewFactory(
+ private val activity: Activity,
+ private val registry: ClockRegistry,
+) {
+ private val clockControllers: HashMap<String, ClockController> = HashMap()
+ private var ticker: TimeTicker? = null
+
+ fun getView(clockId: String): View {
+ return (clockControllers[clockId] ?: initClockController(clockId)).largeClock.view
+ }
+
+ fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
+ clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
+ }
+
+ fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
+ return (clockControllers[clockId] ?: initClockController(clockId))
+ .events
+ .onSeedColorChanged(seedColor)
+ }
+
+ fun registerTimeTicker() {
+ ticker =
+ TimeTicker.registerNewReceiver(activity.applicationContext) {
+ clockControllers.values.forEach { it.largeClock.events.onTimeTick() }
+ }
+ }
+
+ fun unregisterTimeTicker() {
+ activity.applicationContext.unregisterReceiver(ticker)
+ }
+
+ private fun initClockController(clockId: String): ClockController {
+ val controller =
+ registry.createExampleClock(clockId).also { it?.initialize(activity.resources, 0f, 0f) }
+ checkNotNull(controller)
+ val screenSizeCalculator = ScreenSizeCalculator.getInstance()
+ val screenSize = screenSizeCalculator.getScreenSize(activity.windowManager.defaultDisplay)
+ val ratio =
+ activity.resources.getDimensionPixelSize(R.dimen.screen_preview_height).toFloat() /
+ screenSize.y.toFloat()
+ controller.largeClock.events.onFontSettingChanged(
+ activity.resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat() *
+ ratio
+ )
+ clockControllers[clockId] = controller
+ return controller
+ }
+}
diff --git a/src/com/android/customization/picker/clock/ui/view/SaturationProgressDrawable.kt b/src/com/android/customization/picker/clock/ui/view/SaturationProgressDrawable.kt
new file mode 100644
index 00000000..9e1d7890
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/SaturationProgressDrawable.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 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.customization.picker.clock.ui.view
+
+import android.content.pm.ActivityInfo
+import android.content.res.Resources
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.InsetDrawable
+
+/**
+ * [DrawableWrapper] to use in the progress of brightness slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ *
+ * This class also assumes that a "thumb" icon exists within the end's edge of the progress
+ * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb
+ * icon which puts the icon directly underneath the user's finger.
+ */
+class SaturationProgressDrawable @JvmOverloads constructor(drawable: Drawable? = null) :
+ InsetDrawable(drawable, 0) {
+
+ companion object {
+ private const val MAX_LEVEL = 10000
+ }
+
+ override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+ onLevelChange(level)
+ return super.onLayoutDirectionChanged(layoutDirection)
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ onLevelChange(level)
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ val drawableBounds = drawable?.bounds!!
+
+ // The thumb offset shifts the sun icon directly under the user's thumb
+ val thumbOffset = bounds.height() / 2
+ val width = bounds.width() * level / MAX_LEVEL + thumbOffset
+
+ // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width
+ // TODO (b/268541542) Test if RTL devices also works for the slider
+ drawable?.setBounds(
+ bounds.left,
+ drawableBounds.top,
+ width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()),
+ drawableBounds.bottom
+ )
+ return super.onLevelChange(level)
+ }
+
+ override fun getConstantState(): ConstantState {
+ // This should not be null as it was created with a state in the constructor.
+ return RoundedCornerState(super.getConstantState()!!)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme()
+ }
+
+ private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
+ override fun newDrawable(): Drawable {
+ return newDrawable(null, null)
+ }
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable {
+ val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper
+ return SaturationProgressDrawable(wrapper.drawable)
+ }
+
+ override fun getChangingConfigurations(): Int {
+ return wrappedState.changingConfigurations
+ }
+
+ override fun canApplyTheme(): Boolean {
+ return true
+ }
+ }
+}