Merge "Fixes activity leak in Wallpaper Picker." into udc-dev
diff --git a/res/layout/clock_color_option.xml b/res/layout/clock_color_option.xml
index 4203a39..f27c13d 100644
--- a/res/layout/clock_color_option.xml
+++ b/res/layout/clock_color_option.xml
@@ -21,72 +21,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clipChildren="false">
- <FrameLayout
- android:id="@+id/icon_container"
- android:layout_width="@dimen/option_item_size"
- android:layout_height="@dimen/option_item_size"
- android:clipChildren="false">
- <ImageView
- android:id="@id/selection_border"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/option_item_border"
- android:alpha="0"
- android:importantForAccessibility="no" />
-
- <ImageView
- android:id="@id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/option_item_background"
- android:importantForAccessibility="no" />
-
- <FrameLayout
- android:id="@id/foreground"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/color_preview_0"
- android:layout_width="@dimen/component_color_chip_small_radius_default2"
- android:layout_height="@dimen/component_color_chip_small_radius_default2"
- android:layout_gravity="center"
- android:layout_marginRight="@dimen/color_seed_chip_margin2"
- android:layout_marginBottom="@dimen/color_seed_chip_margin2"
- android:src="@drawable/color_chip_seed_filled0"
- android:importantForAccessibility="no"/>
-
- <ImageView
- android:id="@+id/color_preview_1"
- android:layout_width="@dimen/component_color_chip_small_radius_default2"
- android:layout_height="@dimen/component_color_chip_small_radius_default2"
- android:layout_gravity="center"
- android:layout_marginLeft="@dimen/color_seed_chip_margin2"
- android:layout_marginBottom="@dimen/color_seed_chip_margin2"
- android:src="@drawable/color_chip_seed_filled2"
- android:importantForAccessibility="no"/>
-
- <ImageView
- android:id="@+id/color_preview_2"
- android:layout_width="@dimen/component_color_chip_small_radius_default2"
- android:layout_height="@dimen/component_color_chip_small_radius_default2"
- android:layout_gravity="center"
- android:layout_marginRight="@dimen/color_seed_chip_margin2"
- android:layout_marginTop="@dimen/color_seed_chip_margin2"
- android:src="@drawable/color_chip_seed_filled1"
- android:importantForAccessibility="no"/>
-
- <ImageView
- android:id="@+id/color_preview_3"
- android:layout_width="@dimen/component_color_chip_small_radius_default2"
- android:layout_height="@dimen/component_color_chip_small_radius_default2"
- android:layout_gravity="center"
- android:layout_marginLeft="@dimen/color_seed_chip_margin2"
- android:layout_marginTop="@dimen/color_seed_chip_margin2"
- android:src="@drawable/color_chip_seed_filled3"
- android:importantForAccessibility="no" />
- </FrameLayout>
- </FrameLayout>
+ <include layout="@layout/color_option_2"/>
<TextView
android:id="@+id/text"
diff --git a/res/layout/clock_size_radio_button_group.xml b/res/layout/clock_size_radio_button_group.xml
index 4264007..30e39ad 100644
--- a/res/layout/clock_size_radio_button_group.xml
+++ b/res/layout/clock_size_radio_button_group.xml
@@ -57,6 +57,7 @@
android:id="@+id/button_container_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:orientation="horizontal">
<RadioButton
diff --git a/res/layout/color_option_2.xml b/res/layout/color_option_2.xml
index 8bacd99..dff03d0 100644
--- a/res/layout/color_option_2.xml
+++ b/res/layout/color_option_2.xml
@@ -16,7 +16,6 @@
<!-- Content description is set programmatically on the parent FrameLayout -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/option_item_size"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -48,16 +47,5 @@
android:layout_height="match_parent"
android:layout_margin="@dimen/color_seed_chip_margin2"/>
</FrameLayout>
-
- <TextView
- android:id="@+id/text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/option_bottom_margin"
- android:textColor="@color/text_color_primary"
- android:visibility="gone"
- android:gravity="center"
- android:text="Placeholder for stable size calculation, please do not remove."
- tools:ignore="HardcodedText" />
</LinearLayout>
diff --git a/res/layout/color_option_no_background.xml b/res/layout/color_option_no_background.xml
index 6bdbc15..3b8e2dc 100644
--- a/res/layout/color_option_no_background.xml
+++ b/res/layout/color_option_no_background.xml
@@ -52,5 +52,6 @@
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:src="@drawable/color_option_selected_no_background"
- android:visibility="gone"/>
+ android:visibility="gone"
+ android:importantForAccessibility="no" />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/color_option_overflow_no_background.xml b/res/layout/color_option_overflow_no_background.xml
index fb25c35..abb9c6b 100644
--- a/res/layout/color_option_overflow_no_background.xml
+++ b/res/layout/color_option_overflow_no_background.xml
@@ -51,6 +51,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
- android:src="@drawable/color_overflow" />
+ android:src="@drawable/color_overflow"
+ android:contentDescription="@string/more_colors"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4392ebf..16b5de7 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -176,4 +176,10 @@
copied from sysui resources
-->
<dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
+ <!-- Dimension for the clock view, copied from sysui resources. -->
+ <dimen name="small_clock_height">114dp</dimen>
+ <dimen name="small_clock_padding_top">28dp</dimen>
+ <dimen name="clock_padding_start">28dp</dimen>
+
+ <dimen name="tab_touch_delegate_height_padding">8dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1f71bd6..96ff1bf 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -456,6 +456,38 @@
<string name="more_colors">More Colors</string>
<!--
+ Accessibility string for a button that allows the user to select a color scheme generated from
+ their current wallpaper as the system color. This is shown in a list with other color options.
+
+ [CHAR LIMIT=NONE].
+ -->
+ <string name="content_description_dynamic_color_option">Primary dynamic theme</string>
+
+ <!--
+ Accessibility string for a button that allows the user to select a color scheme generated from
+ their current wallpaper as the system color. This is shown in a list with other color options.
+
+ [CHAR LIMIT=NONE].
+ -->
+ <string name="content_description_neutral_color_option">Primary neutral theme</string>
+
+ <!--
+ Accessibility string for a button that allows the user to select a color scheme generated from
+ their current wallpaper as the system color. This is shown in a list with other color options.
+
+ [CHAR LIMIT=NONE].
+ -->
+ <string name="content_description_vibrant_color_option">Primary vibrant theme</string>
+
+ <!--
+ Accessibility string for a button that allows the user to select a color scheme generated from
+ their current wallpaper as the system color. This is shown in a list with other color options.
+
+ [CHAR LIMIT=NONE].
+ -->
+ <string name="content_description_expressive_color_option">Primary expressive theme</string>
+
+ <!--
Accessibility string for a button that allows the user to select the default color for their
lock screen clock on the device. This is shown next to other buttons that allow the user to
select from a set of custom colors.
diff --git a/src/com/android/customization/model/color/ColorOptionImpl.kt b/src/com/android/customization/model/color/ColorOptionImpl.kt
index 70aaa92..3273ce2 100644
--- a/src/com/android/customization/model/color/ColorOptionImpl.kt
+++ b/src/com/android/customization/model/color/ColorOptionImpl.kt
@@ -61,10 +61,7 @@
}
override fun getContentDescription(context: Context): CharSequence? {
- if (type == ColorType.WALLPAPER_COLOR) {
- return context.getString(R.string.wallpaper_color_title)
- }
- return super.getContentDescription(context)
+ return title
}
override fun getSource(): String? {
diff --git a/src/com/android/customization/model/color/ColorProvider.kt b/src/com/android/customization/model/color/ColorProvider.kt
index 5ec919f..6c83f23 100644
--- a/src/com/android/customization/model/color/ColorProvider.kt
+++ b/src/com/android/customization/model/color/ColorProvider.kt
@@ -37,6 +37,7 @@
import com.android.customization.picker.color.shared.model.ColorType
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
+import com.android.wallpaper.R
import com.android.wallpaper.compat.WallpaperManagerCompat
import com.android.wallpaper.module.InjectorProvider
import kotlinx.coroutines.CoroutineScope
@@ -48,7 +49,7 @@
/**
* Default implementation of {@link ColorOptionsProvider} that reads preset colors from a stub APK.
*/
-class ColorProvider(context: Context, stubPackageName: String) :
+class ColorProvider(private val context: Context, stubPackageName: String) :
ResourcesApkProvider(context, stubPackageName), ColorOptionsProvider {
companion object {
@@ -244,6 +245,18 @@
OVERLAY_CATEGORY_SYSTEM_PALETTE,
if (isDefault) "" else toColorString(colorInt)
)
+ builder.title =
+ when (style) {
+ Style.TONAL_SPOT ->
+ context.getString(R.string.content_description_dynamic_color_option)
+ Style.SPRITZ ->
+ context.getString(R.string.content_description_neutral_color_option)
+ Style.VIBRANT ->
+ context.getString(R.string.content_description_vibrant_color_option)
+ Style.EXPRESSIVE ->
+ context.getString(R.string.content_description_expressive_color_option)
+ else -> context.getString(R.string.content_description_dynamic_color_option)
+ }
builder.source = source
builder.style = style
// Color option index value starts from 1.
@@ -350,6 +363,7 @@
when (colorScheme.style) {
Style.FRUIT_SALAD -> intArrayOf(seed, colorScheme.accent1.s200)
Style.TONAL_SPOT -> intArrayOf(colorScheme.accentColor, colorScheme.accentColor)
+ Style.RAINBOW -> intArrayOf(colorScheme.accent1.s200, colorScheme.accent1.s200)
else -> intArrayOf(colorScheme.accent1.s100, colorScheme.accent1.s100)
}
return intArrayOf(
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index c0e4124..dbcff27 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -15,8 +15,8 @@
*/
package com.android.customization.module
-import android.app.Activity
import android.content.Context
+import androidx.activity.ComponentActivity
import androidx.fragment.app.FragmentActivity
import com.android.customization.model.theme.OverlayManagerCompat
import com.android.customization.model.theme.ThemeBundleProvider
@@ -67,7 +67,7 @@
interactor: ClockPickerInteractor,
): ClockCarouselViewModel.Factory
- fun getClockViewFactory(activity: Activity): ClockViewFactory
+ fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory
fun getClockSettingsViewModelFactory(
context: Context,
diff --git a/src/com/android/customization/module/DefaultCustomizationSections.java b/src/com/android/customization/module/DefaultCustomizationSections.java
index 6a5f2f6..520b301 100644
--- a/src/com/android/customization/module/DefaultCustomizationSections.java
+++ b/src/com/android/customization/module/DefaultCustomizationSections.java
@@ -1,5 +1,6 @@
package com.android.customization.module;
+import android.app.WallpaperManager;
import android.os.Bundle;
import androidx.annotation.Nullable;
@@ -100,6 +101,7 @@
DisplayUtils displayUtils,
CustomizationPickerViewModel customizationPickerViewModel,
WallpaperInteractor wallpaperInteractor,
+ WallpaperManager wallpaperManager,
boolean isTwoPaneAndSmallWidth) {
List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>();
@@ -118,6 +120,7 @@
wallpaperPreviewNavigator,
sectionNavigationController,
wallpaperInteractor,
+ wallpaperManager,
isTwoPaneAndSmallWidth)
: new ScreenPreviewSectionController(
activity,
@@ -128,6 +131,7 @@
displayUtils,
wallpaperPreviewNavigator,
wallpaperInteractor,
+ wallpaperManager,
isTwoPaneAndSmallWidth));
sectionControllers.add(
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index 75d88d6..5d9880d 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -15,7 +15,6 @@
*/
package com.android.customization.module
-import android.app.Activity
import android.app.UiModeManager
import android.content.Context
import android.content.Intent
@@ -25,6 +24,8 @@
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import com.android.customization.model.color.ColorCustomizationManager
@@ -80,6 +81,7 @@
import com.android.wallpaper.picker.customization.data.repository.WallpaperRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
+import com.android.wallpaper.util.ScreenSizeCalculator
import kotlinx.coroutines.Dispatchers
open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInjector {
@@ -101,7 +103,7 @@
private var clockPickerInteractor: ClockPickerInteractor? = null
private var clockSectionViewModel: ClockSectionViewModel? = null
private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null
- private var clockViewFactory: ClockViewFactory? = null
+ private var clockViewFactories: MutableMap<Int, ClockViewFactory> = HashMap()
private var notificationsInteractor: NotificationsInteractor? = null
private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
private var colorPickerInteractor: ColorPickerInteractor? = null
@@ -354,9 +356,29 @@
}
}
- override fun getClockViewFactory(activity: Activity): ClockViewFactory {
- return clockViewFactory
- ?: ClockViewFactory(activity, getClockRegistry(activity)).also { clockViewFactory = it }
+ override fun getClockViewFactory(activity: ComponentActivity): ClockViewFactory {
+ val activityHashCode = activity.hashCode()
+ return clockViewFactories[activityHashCode]
+ ?: ClockViewFactory(
+ activity.applicationContext,
+ ScreenSizeCalculator.getInstance()
+ .getScreenSize(activity.windowManager.defaultDisplay),
+ getClockRegistry(
+ activity.applicationContext,
+ ),
+ )
+ .also {
+ clockViewFactories[activityHashCode] = it
+ activity.lifecycle.addObserver(
+ object : DefaultLifecycleObserver {
+ override fun onDestroy(owner: LifecycleOwner) {
+ super.onDestroy(owner)
+ clockViewFactories[activityHashCode]?.onDestroy()
+ clockViewFactories.remove(activityHashCode)
+ }
+ }
+ )
+ }
}
private fun getNotificationsInteractor(
diff --git a/src/com/android/customization/picker/clock/ui/adapter/ClockSettingsTabAdapter.kt b/src/com/android/customization/picker/clock/ui/adapter/ClockSettingsTabAdapter.kt
index 746fdb3..d0e6f18 100644
--- a/src/com/android/customization/picker/clock/ui/adapter/ClockSettingsTabAdapter.kt
+++ b/src/com/android/customization/picker/clock/ui/adapter/ClockSettingsTabAdapter.kt
@@ -16,13 +16,17 @@
*/
package com.android.customization.picker.clock.ui.adapter
+import android.graphics.Rect
import android.view.LayoutInflater
+import android.view.TouchDelegate
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsTabViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.util.ViewUtils.setupTouchDelegate
+
/** Adapter for the tab recycler view on the clock settings screen. */
class ClockSettingsTabAdapter : RecyclerView.Adapter<ClockSettingsTabAdapter.ViewHolder>() {
@@ -54,6 +58,10 @@
val item = items[position]
holder.itemView.isSelected = item.isSelected
holder.textView.text = item.name
+ holder.textView.setupTouchDelegate(
+ parentView = holder.itemView,
+ heightRes = R.dimen.tab_touch_delegate_height_padding
+ )
holder.textView.setOnClickListener(
if (item.onClicked != null) {
View.OnClickListener { item.onClicked.invoke() }
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
index c95561c..5323cf4 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -91,7 +91,7 @@
launch {
viewModel.clockId.collect { clockId ->
singleClockHostView.removeAllViews()
- val clockView = clockViewFactory.getView(clockId)
+ val clockView = clockViewFactory.getLargeView(clockId)
// The clock view might still be attached to an existing parent. Detach
// before adding to another parent.
(clockView.parent as? ViewGroup)?.removeView(clockView)
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
index 921151b..671a7ae 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -21,6 +21,7 @@
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.SeekBar
+import androidx.core.view.doOnPreDraw
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
@@ -40,6 +41,7 @@
import com.android.customization.picker.common.ui.view.ItemSpacing
import com.android.wallpaper.R
import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -91,17 +93,6 @@
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.selectedClockId
- .mapNotNull { it }
- .collect { clockId ->
- val clockView = clockViewFactory.getView(clockId)
- (clockView.parent as? ViewGroup)?.removeView(clockView)
- clockHostView.removeAllViews()
- clockHostView.addView(clockView)
- }
- }
-
- launch {
viewModel.seedColor.collect { seedColor ->
viewModel.selectedClockId.value?.let { selectedClockId ->
clockViewFactory.updateColor(selectedClockId, seedColor)
@@ -133,7 +124,7 @@
val item =
LayoutInflater.from(view.context)
.inflate(
- R.layout.color_option_2,
+ R.layout.clock_color_option,
colorOptionContainerListView,
false,
) as LinearLayout
@@ -176,18 +167,41 @@
}
launch {
- viewModel.selectedClockSize.collect { size ->
- when (size) {
- ClockSize.DYNAMIC -> {
- sizeOptions.radioButtonDynamic.isChecked = true
- sizeOptions.radioButtonSmall.isChecked = false
- }
- ClockSize.SMALL -> {
- sizeOptions.radioButtonDynamic.isChecked = false
- sizeOptions.radioButtonSmall.isChecked = true
+ combine(
+ viewModel.selectedClockId.mapNotNull { it },
+ viewModel.selectedClockSize,
+ ::Pair,
+ )
+ .collect { (clockId, size) ->
+ val clockView =
+ if (size == ClockSize.DYNAMIC) {
+ clockViewFactory.getLargeView(clockId)
+ } else {
+ clockViewFactory.getSmallView(clockId)
+ }
+ (clockView.parent as? ViewGroup)?.removeView(clockView)
+ clockHostView.removeAllViews()
+ clockHostView.addView(clockView)
+
+ when (size) {
+ ClockSize.DYNAMIC -> {
+ sizeOptions.radioButtonDynamic.isChecked = true
+ sizeOptions.radioButtonSmall.isChecked = false
+ clockHostView.doOnPreDraw {
+ it.pivotX = (it.width / 2).toFloat()
+ it.pivotY = (it.height / 2).toFloat()
+ }
+ }
+ ClockSize.SMALL -> {
+ sizeOptions.radioButtonDynamic.isChecked = false
+ sizeOptions.radioButtonSmall.isChecked = true
+ clockHostView.doOnPreDraw {
+ it.pivotX = 0F
+ it.pivotY = 0F
+ }
+ }
}
}
- }
}
launch {
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
index 88d6d8e..7edaecf 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -79,7 +79,7 @@
R.string.lock_screen_preview_provider_authority,
),
),
- wallpaperInfoProvider = {
+ wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
injector
.getCurrentWallpaperInfoFactory(context)
@@ -90,7 +90,7 @@
null,
)
},
- /* forceRefresh= */ true,
+ forceReload,
)
}
},
@@ -134,6 +134,10 @@
}
override fun getDefaultTitle(): CharSequence {
- return requireContext().getString(R.string.clock_settings_title)
+ return requireContext().getString(R.string.clock_color_and_size_title)
+ }
+
+ override fun getToolbarColorId(): Int {
+ return android.R.color.transparent
}
}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
index d085b7b..cfc05dc 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -15,35 +15,76 @@
*/
package com.android.customization.picker.clock.ui.view
-import android.app.Activity
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
import android.graphics.Rect
import android.util.TypedValue
import android.view.View
+import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.WeatherData
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
import java.util.concurrent.ConcurrentHashMap
+/**
+ * Provide reusable clock view and related util functions.
+ *
+ * @property screenSize The Activity or Fragment's window size.
+ */
class ClockViewFactory(
- private val activity: Activity,
+ private val appContext: Context,
+ val screenSize: Point,
private val registry: ClockRegistry,
) {
+ private val resources = appContext.resources
private val timeTickListeners: ConcurrentHashMap<Int, TimeTicker> = ConcurrentHashMap()
private val clockControllers: HashMap<String, ClockController> = HashMap()
+ private val smallClockFrames: HashMap<String, FrameLayout> = HashMap()
fun getController(clockId: String): ClockController {
- return clockControllers[clockId] ?: initClockController(clockId)
+ return clockControllers[clockId]
+ ?: initClockController(clockId).also { clockControllers[clockId] = it }
}
- fun getView(clockId: String): View {
+ fun getLargeView(clockId: String): View {
return getController(clockId).largeClock.view
}
+ fun getSmallView(clockId: String): View {
+ return smallClockFrames[clockId]
+ ?: createSmallClockFrame().also {
+ it.addView(getController(clockId).smallClock.view)
+ smallClockFrames[clockId] = it
+ }
+ }
+
+ private fun createSmallClockFrame(): FrameLayout {
+ val smallClockFrame = FrameLayout(appContext)
+ val layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ resources.getDimensionPixelSize(R.dimen.small_clock_height)
+ )
+ layoutParams.topMargin =
+ getStatusBarHeight(resources) +
+ resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
+ smallClockFrame.layoutParams = layoutParams
+
+ smallClockFrame.setPaddingRelative(
+ resources.getDimensionPixelSize(R.dimen.clock_padding_start),
+ 0,
+ 0,
+ 0
+ )
+ smallClockFrame.clipChildren = false
+ return smallClockFrame
+ }
+
fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
}
@@ -57,7 +98,7 @@
fun updateTimeFormat(clockId: String) {
getController(clockId)
.events
- .onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(activity))
+ .onTimeFormatChanged(android.text.format.DateFormat.is24HourFormat(appContext))
}
fun registerTimeTicker(owner: LifecycleOwner) {
@@ -66,37 +107,53 @@
return
}
- timeTickListeners[hashCode] =
- TimeTicker.registerNewReceiver(activity.applicationContext) { onTimeTick() }
+ timeTickListeners[hashCode] = TimeTicker.registerNewReceiver(appContext) { onTimeTick() }
+ }
+
+ fun onDestroy() {
+ timeTickListeners.forEach { (_, timeTicker) -> appContext.unregisterReceiver(timeTicker) }
+ timeTickListeners.clear()
+ clockControllers.clear()
+ smallClockFrames.clear()
}
private fun onTimeTick() {
- clockControllers.values.forEach { it.largeClock.events.onTimeTick() }
+ clockControllers.values.forEach {
+ it.largeClock.events.onTimeTick()
+ it.smallClock.events.onTimeTick()
+ }
}
fun unregisterTimeTicker(owner: LifecycleOwner) {
val hashCode = owner.hashCode()
timeTickListeners[hashCode]?.let {
- activity.applicationContext.unregisterReceiver(it)
+ appContext.unregisterReceiver(it)
timeTickListeners.remove(hashCode)
}
}
private fun initClockController(clockId: String): ClockController {
val controller =
- registry.createExampleClock(clockId).also { it?.initialize(activity.resources, 0f, 0f) }
+ registry.createExampleClock(clockId).also { it?.initialize(resources, 0f, 0f) }
checkNotNull(controller)
// Configure light/dark theme
val isLightTheme = TypedValue()
- activity.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
+ appContext.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
val isRegionDark = isLightTheme.data == 0
controller.largeClock.events.onRegionDarknessChanged(isRegionDark)
// Configure font size
controller.largeClock.events.onFontSettingChanged(
- activity.resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
controller.largeClock.events.onTargetRegionChanged(getLargeClockRegion())
+
+ controller.smallClock.events.onRegionDarknessChanged(isRegionDark)
+ controller.smallClock.events.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ )
+ controller.smallClock.events.onTargetRegionChanged(getSmallClockRegion())
+
// Use placeholder for weather clock preview in picker
controller.events.onWeatherDataChanged(
WeatherData(
@@ -106,7 +163,6 @@
useCelsius = USE_CELSIUS_PLACEHODLER,
)
)
- clockControllers[clockId] = controller
return controller
}
@@ -115,21 +171,41 @@
* proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
* and position the clock view
*/
- fun getLargeClockRegion(): Rect {
- val screenSizeCalculator = ScreenSizeCalculator.getInstance()
- val screenSize = screenSizeCalculator.getScreenSize(activity.windowManager.defaultDisplay)
+ private fun getLargeClockRegion(): Rect {
val largeClockTopMargin =
- activity.resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
- val targetHeight =
- activity.resources.getDimensionPixelSize(R.dimen.large_clock_text_size) * 2
+ resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
+ val targetHeight = resources.getDimensionPixelSize(R.dimen.large_clock_text_size) * 2
val top = (screenSize.y / 2 - targetHeight / 2 + largeClockTopMargin / 2)
return Rect(0, top, screenSize.x, (top + targetHeight))
}
+ /**
+ * Simulate the function of getSmallClockRegion in KeyguardClockSwitch so that we can get a
+ * proper region corresponding to lock screen in picker and for onTargetRegionChanged to scale
+ * and position the clock view
+ */
+ private fun getSmallClockRegion(): Rect {
+ val topMargin =
+ getStatusBarHeight(resources) +
+ resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
+ val start = resources.getDimensionPixelSize(R.dimen.clock_padding_start)
+ val targetHeight = resources.getDimensionPixelSize(R.dimen.small_clock_height)
+ return Rect(start, topMargin, screenSize.x, topMargin + targetHeight)
+ }
+
companion object {
- val DESCRIPTION_PLACEHODLER = ""
- val TEMPERATURE_PLACEHOLDER = 58
+ const val DESCRIPTION_PLACEHODLER = ""
+ const val TEMPERATURE_PLACEHOLDER = 58
val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
- val USE_CELSIUS_PLACEHODLER = false
+ const val USE_CELSIUS_PLACEHODLER = false
+
+ private fun getStatusBarHeight(resource: Resources): Int {
+ var result = 0
+ val resourceId: Int = resource.getIdentifier("status_bar_height", "dimen", "android")
+ if (resourceId > 0) {
+ result = resource.getDimensionPixelSize(resourceId)
+ }
+ return result
+ }
}
}
diff --git a/src/com/android/customization/picker/color/ColorPickerFragment.kt b/src/com/android/customization/picker/color/ColorPickerFragment.kt
deleted file mode 100644
index c8ecb7f..0000000
--- a/src/com/android/customization/picker/color/ColorPickerFragment.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.color
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.android.wallpaper.R
-import com.android.wallpaper.picker.AppbarFragment
-
-// TODO (b/262924623): Color Picker Fragment
-class ColorPickerFragment : AppbarFragment() {
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- val view =
- inflater.inflate(
- R.layout.fragment_color_picker,
- container,
- false,
- )
- setUpToolbar(view)
- return view
- }
-}
diff --git a/src/com/android/customization/picker/color/ui/adapter/ColorTypeTabAdapter.kt b/src/com/android/customization/picker/color/ui/adapter/ColorTypeTabAdapter.kt
index bb9f082..ac5ad81 100644
--- a/src/com/android/customization/picker/color/ui/adapter/ColorTypeTabAdapter.kt
+++ b/src/com/android/customization/picker/color/ui/adapter/ColorTypeTabAdapter.kt
@@ -17,13 +17,16 @@
package com.android.customization.picker.color.ui.adapter
+import android.graphics.Rect
import android.view.LayoutInflater
+import android.view.TouchDelegate
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.customization.picker.color.ui.viewmodel.ColorTypeTabViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.util.ViewUtils.setupTouchDelegate
/** Adapts between color type items and views. */
class ColorTypeTabAdapter : RecyclerView.Adapter<ColorTypeTabAdapter.ViewHolder>() {
@@ -55,6 +58,10 @@
val item = items[position]
holder.itemView.isSelected = item.isSelected
holder.textView.text = item.name
+ holder.textView.setupTouchDelegate(
+ parentView = holder.itemView,
+ heightRes = R.dimen.tab_touch_delegate_height_padding
+ )
holder.textView.setOnClickListener(
if (item.onClick != null) {
View.OnClickListener { item.onClick.invoke() }
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
index 16cdd75..45e3cde 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorSectionViewBinder.kt
@@ -30,6 +30,7 @@
import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.picker.common.icon.ui.viewbinder.ContentDescriptionViewBinder
import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
import kotlinx.coroutines.launch
@@ -103,6 +104,10 @@
item.payload,
night
)
+ ContentDescriptionViewBinder.bind(
+ view = itemView.requireViewById(R.id.option_tile),
+ viewModel = item.text,
+ )
}
val optionSelectedView = itemView.findViewById<ImageView>(R.id.option_selected)
diff --git a/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt b/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
index 23ad037..ef38f8b 100644
--- a/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
+++ b/src/com/android/customization/picker/color/ui/fragment/ColorPickerFragment.kt
@@ -98,13 +98,13 @@
R.string.lock_screen_preview_provider_authority,
),
),
- wallpaperInfoProvider = {
+ wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
{ homeWallpaper, lockWallpaper, _ ->
continuation.resume(lockWallpaper ?: homeWallpaper, null)
},
- /* forceRefresh= */ true,
+ forceReload,
)
}
},
@@ -133,13 +133,13 @@
R.string.grid_control_metadata_name,
),
),
- wallpaperInfoProvider = {
+ wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
{ homeWallpaper, lockWallpaper, _ ->
continuation.resume(homeWallpaper ?: lockWallpaper, null)
},
- /* forceRefresh= */ true,
+ forceReload,
)
}
},
diff --git a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
index 4bd5f1c..5ad01a8 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -17,6 +17,7 @@
package com.android.customization.picker.preview.ui.section
+import android.app.WallpaperManager
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
@@ -65,6 +66,7 @@
wallpaperPreviewNavigator: WallpaperPreviewNavigator,
private val navigationController: CustomizationSectionNavigationController,
wallpaperInteractor: WallpaperInteractor,
+ wallpaperManager: WallpaperManager,
private val isTwoPaneAndSmallWidth: Boolean,
) :
ScreenPreviewSectionController(
@@ -76,6 +78,7 @@
displayUtils,
wallpaperPreviewNavigator,
wallpaperInteractor,
+ wallpaperManager,
isTwoPaneAndSmallWidth,
) {
diff --git a/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
index 5203ed3..b34ea1b 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
@@ -17,13 +17,16 @@
package com.android.customization.picker.quickaffordance.ui.adapter
+import android.graphics.Rect
import android.view.LayoutInflater
+import android.view.TouchDelegate
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.util.ViewUtils.setupTouchDelegate
/** Adapts between lock screen quick affordance slot items and views. */
class SlotTabAdapter : RecyclerView.Adapter<SlotTabAdapter.ViewHolder>() {
@@ -55,6 +58,10 @@
val item = items[position]
holder.itemView.isSelected = item.isSelected
holder.textView.text = item.name
+ holder.textView.setupTouchDelegate(
+ parentView = holder.itemView,
+ heightRes = R.dimen.tab_touch_delegate_height_padding
+ )
holder.textView.setOnClickListener(
if (item.onClicked != null) {
View.OnClickListener { item.onClicked.invoke() }
diff --git a/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
index 05845f8..525c657 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
@@ -90,13 +90,13 @@
)
}
},
- wallpaperInfoProvider = {
+ wallpaperInfoProvider = { forceReload ->
suspendCancellableCoroutine { continuation ->
wallpaperInfoFactory.createCurrentWallpaperInfos(
{ homeWallpaper, lockWallpaper, _ ->
continuation.resume(lockWallpaper ?: homeWallpaper, null)
},
- /* forceRefresh= */ true,
+ forceReload,
)
}
},