Merge "Keep wallpaper quick switch order stable (2/3)." into tm-qpr-dev
diff --git a/res/drawable/ic_clock_24px.xml b/res/drawable/ic_clock_24px.xml
index b3f1fee..946bc51 100644
--- a/res/drawable/ic_clock_24px.xml
+++ b/res/drawable/ic_clock_24px.xml
@@ -20,6 +20,6 @@
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:textColorPrimary"
android:pathData="M15.3,16.7 L16.7,15.3 13,11.6V7H11V12.4ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12ZM12,20Q15.325,20 17.663,17.663Q20,15.325 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20Z"/>
</vector>
\ No newline at end of file
diff --git a/res/layout/clock_section_view.xml b/res/layout/clock_section_view.xml
index 8512498..4a651ca 100644
--- a/res/layout/clock_section_view.xml
+++ b/res/layout/clock_section_view.xml
@@ -33,10 +33,11 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/clock_title"
+ android:text="@string/clock_settings_title"
style="@style/SectionTitleTextStyle" />
<TextView
+ android:id="@+id/selected_clock_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/clock_description"
@@ -49,7 +50,5 @@
android:scaleType="center"
android:src="@drawable/ic_clock_24px"
android:background="@drawable/option_border_color"
- android:contentDescription="@string/clock_picker_entry_content_description"
- android:tint="@color/text_color_primary" />
-
+ android:contentDescription="@string/clock_picker_entry_content_description" />
</com.android.customization.picker.clock.ClockSectionView>
\ No newline at end of file
diff --git a/res/layout/fragment_clock_custom_picker_demo.xml b/res/layout/fragment_clock_custom_picker_demo.xml
new file mode 100644
index 0000000..c05c4a8
--- /dev/null
+++ b/res/layout/fragment_clock_custom_picker_demo.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/section_header_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toTopOf="@+id/clock_preview_card_list_demo"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <include layout="@layout/section_header" />
+ </FrameLayout>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/clock_preview_card_list_demo"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/section_header_container"
+ android:clipToPadding="false" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 13fc8a0..125bda1 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -33,6 +33,9 @@
<!-- The content description of clock entry. [CHAR LIMIT=NONE] -->
<string name="clock_picker_entry_content_description">Change a custom clock</string>
+ <!-- Title of a section of the customization picker where the user can configure Clock face. [CHAR LIMIT=15] -->
+ <string name="clock_settings_title">Clock Settings</string>
+
<!-- Title of a section of the customization picker where the user can select a Grid size for
the home screen. [CHAR LIMIT=15] -->
<string name="grid_title">App grid</string>
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 3cf8393..2772e56 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -21,6 +21,8 @@
import com.android.customization.model.theme.ThemeBundleProvider
import com.android.customization.model.theme.ThemeManager
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.shared.clocks.ClockRegistry
import com.android.wallpaper.module.Injector
interface CustomizationInjector : Injector {
@@ -36,4 +38,8 @@
fun getKeyguardQuickAffordancePickerInteractor(
context: Context
): KeyguardQuickAffordancePickerInteractor
+
+ fun getClockRegistry(context: Context): ClockRegistry
+
+ fun getPluginManager(context: Context): PluginManager
}
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index ab6b673..5b43654 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -16,10 +16,15 @@
package com.android.customization.module
import android.app.Activity
+import android.app.NotificationManager
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
+import android.os.Handler
+import android.os.UserHandle
+import android.view.LayoutInflater
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.android.customization.model.theme.OverlayManagerCompat
@@ -29,8 +34,18 @@
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer
import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.systemui.plugins.Plugin
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.systemui.shared.clocks.DefaultClockProvider
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
+import com.android.systemui.shared.plugins.PluginActionManager
+import com.android.systemui.shared.plugins.PluginEnabler
+import com.android.systemui.shared.plugins.PluginInstance
+import com.android.systemui.shared.plugins.PluginManagerImpl
+import com.android.systemui.shared.plugins.PluginPrefs
+import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager_Factory
import com.android.wallpaper.model.LiveWallpaperInfo
import com.android.wallpaper.model.WallpaperInfo
import com.android.wallpaper.module.CustomizationSections
@@ -43,6 +58,7 @@
import com.android.wallpaper.picker.LivePreviewFragment
import com.android.wallpaper.picker.PreviewFragment
import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
+import java.util.concurrent.Executors
import kotlinx.coroutines.Dispatchers.IO
open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInjector {
@@ -58,6 +74,8 @@
private var fragmentFactory: FragmentFactory? = null
private var keyguardQuickAffordanceSnapshotRestorer: KeyguardQuickAffordanceSnapshotRestorer? =
null
+ private var clockRegistry: ClockRegistry? = null
+ private var pluginManager: PluginManager? = null
override fun getCustomizationSections(activity: Activity): CustomizationSections {
return customizationSections
@@ -191,6 +209,80 @@
.also { keyguardQuickAffordanceSnapshotRestorer = it }
}
+ override fun getClockRegistry(context: Context): ClockRegistry {
+ return clockRegistry
+ ?: ClockRegistry(
+ context,
+ getPluginManager(context),
+ Handler.getMain(),
+ isEnabled = true,
+ userHandle = UserHandle.USER_SYSTEM,
+ DefaultClockProvider(context, LayoutInflater.from(context), context.resources)
+ )
+ .also { clockRegistry = it }
+ }
+
+ override fun getPluginManager(context: Context): PluginManager {
+ return pluginManager ?: createPluginManager(context).also { pluginManager = it }
+ }
+
+ private fun createPluginManager(context: Context): PluginManager {
+ val privilegedPlugins = listOf<String>()
+ val isDebugDevice = true
+
+ val instanceFactory =
+ PluginInstance.Factory(
+ this::class.java.classLoader,
+ PluginInstance.InstanceFactory<Plugin>(),
+ PluginInstance.VersionChecker(),
+ privilegedPlugins,
+ isDebugDevice,
+ )
+
+ /*
+ * let SystemUI handle plugin, in this class assume plugins are enabled
+ */
+ val pluginEnabler =
+ object : PluginEnabler {
+ override fun setEnabled(component: ComponentName) = Unit
+
+ override fun setDisabled(
+ component: ComponentName,
+ @PluginEnabler.DisableReason reason: Int
+ ) = Unit
+
+ override fun isEnabled(component: ComponentName): Boolean {
+ return true
+ }
+
+ @PluginEnabler.DisableReason
+ override fun getDisableReason(componentName: ComponentName): Int {
+ return PluginEnabler.ENABLED
+ }
+ }
+
+ val pluginActionManager =
+ PluginActionManager.Factory(
+ context,
+ context.packageManager,
+ context.mainExecutor,
+ Executors.newSingleThreadExecutor(),
+ context.getSystemService(NotificationManager::class.java),
+ pluginEnabler,
+ privilegedPlugins,
+ instanceFactory,
+ )
+ return PluginManagerImpl(
+ context,
+ pluginActionManager,
+ isDebugDevice,
+ UncaughtExceptionPreHandlerManager_Factory.create().get(),
+ pluginEnabler,
+ PluginPrefs(context),
+ listOf(),
+ )
+ }
+
companion object {
@JvmStatic
private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER =
diff --git a/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt b/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt
index 8648dca..4f65080 100644
--- a/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt
+++ b/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt
@@ -1,11 +1,8 @@
package com.android.customization.picker.clock
-import android.app.NotificationManager
-import android.content.ComponentName
import android.content.Context
import android.os.Bundle
-import android.os.Handler
-import android.os.UserHandle
+import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
@@ -14,36 +11,23 @@
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.TextView
+import android.widget.Toast
import androidx.core.view.setPadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.customization.module.ThemePickerInjector
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
-import com.android.systemui.plugins.Plugin
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.shared.clocks.DefaultClockProvider
-import com.android.systemui.shared.plugins.PluginActionManager
-import com.android.systemui.shared.plugins.PluginEnabler
-import com.android.systemui.shared.plugins.PluginEnabler.ENABLED
-import com.android.systemui.shared.plugins.PluginInstance
-import com.android.systemui.shared.plugins.PluginManagerImpl
-import com.android.systemui.shared.plugins.PluginPrefs
-import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager_Factory
import com.android.wallpaper.R
+import com.android.wallpaper.module.InjectorProvider
import com.android.wallpaper.picker.AppbarFragment
-import java.util.concurrent.Executors
-
-private val TAG = ClockCustomDemoFragment::class.simpleName
class ClockCustomDemoFragment : AppbarFragment() {
@VisibleForTesting lateinit var clockRegistry: ClockRegistry
- val isDebugDevice = true
- val privilegedPlugins = listOf<String>()
- val action = ClockProviderPlugin.ACTION
- lateinit var view: ViewGroup
@VisibleForTesting lateinit var recyclerView: RecyclerView
lateinit var pluginManager: PluginManager
@VisibleForTesting
@@ -51,24 +35,19 @@
object : PluginListener<ClockProviderPlugin> {
override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) {
val listInUse = clockRegistry.getClocks().filter { "NOT_IN_USE" !in it.clockId }
- recyclerView.adapter = ClockRecyclerAdapter(listInUse, context, clockRegistry)
+ recyclerView.adapter =
+ ClockRecyclerAdapter(listInUse, context) {
+ clockRegistry.currentClockId = it.clockId
+ Toast.makeText(context, "${it.name} selected", Toast.LENGTH_SHORT).show()
+ }
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
- val defaultClockProvider =
- DefaultClockProvider(context, LayoutInflater.from(context), context.resources)
- pluginManager = createPluginManager(context)
- clockRegistry =
- ClockRegistry(
- context,
- pluginManager,
- Handler.getMain(),
- isEnabled = true,
- userHandle = UserHandle.USER_OWNER,
- defaultClockProvider
- )
+ val injector = InjectorProvider.getInjector() as ThemePickerInjector
+ pluginManager = injector.getPluginManager(context)
+ clockRegistry = injector.getClockRegistry(context)
pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java, true)
}
@@ -92,69 +71,10 @@
return getString(R.string.clock_title)
}
- private fun createPluginManager(context: Context): PluginManager {
- val instanceFactory =
- PluginInstance.Factory(
- this::class.java.classLoader,
- PluginInstance.InstanceFactory<Plugin>(),
- PluginInstance.VersionChecker(),
- privilegedPlugins,
- isDebugDevice
- )
-
- /*
- * let SystemUI handle plugin, in this class assume plugins are enabled
- */
- val pluginEnabler =
- object : PluginEnabler {
- override fun setEnabled(component: ComponentName) {}
-
- override fun setDisabled(
- component: ComponentName,
- @PluginEnabler.DisableReason reason: Int
- ) {}
-
- override fun isEnabled(component: ComponentName): Boolean {
- return true
- }
-
- @PluginEnabler.DisableReason
- override fun getDisableReason(componentName: ComponentName): Int {
- return ENABLED
- }
- }
-
- val pluginActionManager =
- PluginActionManager.Factory(
- context,
- context.packageManager,
- context.mainExecutor,
- Executors.newSingleThreadExecutor(),
- context.getSystemService(NotificationManager::class.java),
- pluginEnabler,
- privilegedPlugins,
- instanceFactory
- )
- return PluginManagerImpl(
- context,
- pluginActionManager,
- isDebugDevice,
- uncaughtExceptionPreHandlerManager,
- pluginEnabler,
- PluginPrefs(context),
- listOf()
- )
- }
-
- companion object {
- private val uncaughtExceptionPreHandlerManager =
- UncaughtExceptionPreHandlerManager_Factory.create().get()
- }
-
internal class ClockRecyclerAdapter(
val list: List<ClockMetadata>,
val context: Context,
- val clockRegistry: ClockRegistry
+ val onClockSelected: (ClockMetadata) -> Unit
) : RecyclerView.Adapter<ClockRecyclerAdapter.ViewHolder>() {
class ViewHolder(val view: View, val textView: TextView, val onItemClicked: (Int) -> Unit) :
RecyclerView.ViewHolder(view) {
@@ -169,13 +89,12 @@
TextView(ContextThemeWrapper(viewGroup.context, R.style.SectionTitleTextStyle))
textView.setPadding(ITEM_PADDING)
rootView.addView(textView)
+ val outValue = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
+ rootView.setBackgroundResource(outValue.resourceId)
val lp = RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
- rootView.setLayoutParams(lp)
- return ViewHolder(
- rootView,
- textView,
- { clockRegistry.currentClockId = list[it].clockId }
- )
+ rootView.layoutParams = lp
+ return ViewHolder(rootView, textView) { onClockSelected(list[it]) }
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
new file mode 100644
index 0000000..52c2430
--- /dev/null
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.data.repository
+
+import android.util.Log
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
+import com.android.systemui.plugins.ClockMetadata
+import com.android.systemui.shared.clocks.ClockRegistry
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+
+/**
+ * Repository for accessing application clock settings, as well as selecting and configuring custom
+ * clocks.
+ */
+class ClockPickerRepository(registry: ClockRegistry) {
+
+ /** The currently-selected clock. */
+ val selectedClock: Flow<ClockMetadataModel?> = callbackFlow {
+ fun send() {
+ val model =
+ registry
+ .getClocks()
+ .find { clockMetadata -> clockMetadata.clockId == registry.currentClockId }
+ ?.toModel()
+ if (model == null) {
+ Log.e(TAG, "Currently selected clock ID is not one of the available clocks.")
+ }
+ trySend(model)
+ }
+
+ val listener = ClockRegistry.ClockChangeListener { send() }
+ registry.registerClockChangeListener(listener)
+ send()
+ awaitClose { registry.unregisterClockChangeListener(listener) }
+ }
+
+ private fun ClockMetadata.toModel(): ClockMetadataModel {
+ return ClockMetadataModel(clockId = clockId, name = name)
+ }
+
+ companion object {
+ private const val TAG = "ClockPickerRepository"
+ }
+}
diff --git a/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
new file mode 100644
index 0000000..63b3638
--- /dev/null
+++ b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.domain.interactor
+
+import com.android.customization.picker.clock.data.repository.ClockPickerRepository
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Interactor for accessing application clock settings, as well as selecting and configuring custom
+ * clocks.
+ */
+class ClockPickerInteractor(private val repository: ClockPickerRepository) {
+
+ val selectedClock: Flow<ClockMetadataModel?> = repository.selectedClock
+}
diff --git a/src/com/android/customization/picker/clock/domain/interactor/ClocksSnapshotRestorer.kt b/src/com/android/customization/picker/clock/domain/interactor/ClocksSnapshotRestorer.kt
index 63b4a9b..904d2f9 100644
--- a/src/com/android/customization/picker/clock/domain/interactor/ClocksSnapshotRestorer.kt
+++ b/src/com/android/customization/picker/clock/domain/interactor/ClocksSnapshotRestorer.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
new file mode 100644
index 0000000..ea84d2b
--- /dev/null
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.shared.model
+
+/** Model for clock metadata. */
+data class ClockMetadataModel(
+ val clockId: String,
+ val name: String,
+)
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.kt
new file mode 100644
index 0000000..5a3286d
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSectionViewBinder.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.binder
+
+import android.view.View
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.flowWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
+import com.android.wallpaper.R
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+object ClockSectionViewBinder {
+ fun bind(
+ view: View,
+ viewModel: ClockSectionViewModel,
+ lifecycleOwner: LifecycleOwner,
+ onClicked: () -> Unit,
+ ) {
+ view.setOnClickListener { onClicked() }
+
+ val selectedClockTextView: TextView = view.requireViewById(R.id.selected_clock_text)
+
+ lifecycleOwner.lifecycleScope.launch {
+ viewModel.selectedClockName
+ .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
+ .collectLatest { selectedClockName ->
+ selectedClockTextView.text = selectedClockName
+ }
+ }
+ }
+}
diff --git a/src/com/android/customization/model/clock/ClockSectionController.kt b/src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt
similarity index 78%
rename from src/com/android/customization/model/clock/ClockSectionController.kt
rename to src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt
index 1e339bb..848f226 100644
--- a/src/com/android/customization/model/clock/ClockSectionController.kt
+++ b/src/com/android/customization/picker/clock/ui/section/ClockSectionController.kt
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.customization.model.clock
+package com.android.customization.picker.clock.ui.section
import android.content.Context
import android.view.LayoutInflater
+import androidx.lifecycle.LifecycleOwner
import com.android.customization.picker.clock.ClockCustomDemoFragment
import com.android.customization.picker.clock.ClockSectionView
+import com.android.customization.picker.clock.ui.binder.ClockSectionViewBinder
+import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.wallpaper.R
@@ -30,6 +33,8 @@
class ClockSectionController(
private val navigationController: CustomizationSectionNavigationController,
private val customizationProviderClient: CustomizationProviderClient,
+ private val viewModel: ClockSectionViewModel,
+ private val lifecycleOwner: LifecycleOwner,
) : CustomizationSectionController<ClockSectionView?> {
override fun isAvailable(context: Context?): Boolean {
return runBlocking { customizationProviderClient.queryFlags() }
@@ -44,7 +49,11 @@
R.layout.clock_section_view,
null,
) as ClockSectionView
- view.setOnClickListener { navigationController.navigateTo(ClockCustomDemoFragment()) }
+ ClockSectionViewBinder.bind(
+ view = view,
+ viewModel = viewModel,
+ lifecycleOwner = lifecycleOwner
+ ) { navigationController.navigateTo(ClockCustomDemoFragment()) }
return view
}
}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
new file mode 100644
index 0000000..f50bb9c
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSectionViewModel.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.viewmodel
+
+import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** View model for the clock section view on the lockscreen customization surface. */
+class ClockSectionViewModel(interactor: ClockPickerInteractor) {
+
+ val selectedClockName: Flow<String?> = interactor.selectedClock.map { it?.name }
+}
diff --git a/tests/src/com/android/customization/testing/TestCustomizationInjector.java b/tests/src/com/android/customization/testing/TestCustomizationInjector.java
deleted file mode 100644
index d609335..0000000
--- a/tests/src/com/android/customization/testing/TestCustomizationInjector.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.android.customization.testing;
-
-import android.content.Context;
-
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.customization.model.theme.OverlayManagerCompat;
-import com.android.customization.model.theme.ThemeBundleProvider;
-import com.android.customization.model.theme.ThemeManager;
-import com.android.customization.module.CustomizationInjector;
-import com.android.customization.module.CustomizationPreferences;
-import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository;
-import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
-import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer;
-import com.android.systemui.shared.customization.data.content.CustomizationProviderClient;
-import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl;
-import com.android.wallpaper.config.BaseFlags;
-import com.android.wallpaper.module.DrawableLayerResolver;
-import com.android.wallpaper.module.PackageStatusNotifier;
-import com.android.wallpaper.module.UserEventLogger;
-import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer;
-import com.android.wallpaper.testing.TestInjector;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import kotlinx.coroutines.Dispatchers;
-
-/**
- * Test implementation of the dependency injector.
- */
-public class TestCustomizationInjector extends TestInjector implements CustomizationInjector {
- private CustomizationPreferences mCustomizationPreferences;
- private ThemeManager mThemeManager;
- private PackageStatusNotifier mPackageStatusNotifier;
- private DrawableLayerResolver mDrawableLayerResolver;
- private UserEventLogger mUserEventLogger;
- private KeyguardQuickAffordancePickerInteractor mKeyguardQuickAffordancePickerInteractor;
- private BaseFlags mFlags;
- private CustomizationProviderClient mCustomizationProviderClient;
- private KeyguardQuickAffordanceSnapshotRestorer mKeyguardQuickAffordanceSnapshotRestorer;
-
- @Override
- public CustomizationPreferences getCustomizationPreferences(Context context) {
- if (mCustomizationPreferences == null) {
- mCustomizationPreferences = new TestDefaultCustomizationPreferences(context);
- }
- return mCustomizationPreferences;
- }
-
- @Override
- public ThemeManager getThemeManager(
- ThemeBundleProvider provider,
- FragmentActivity activity,
- OverlayManagerCompat overlayManagerCompat,
- ThemesUserEventLogger logger) {
- if (mThemeManager == null) {
- mThemeManager = new TestThemeManager(provider, activity, overlayManagerCompat, logger);
- }
- return mThemeManager;
- }
-
- @Override
- public PackageStatusNotifier getPackageStatusNotifier(Context context) {
- if (mPackageStatusNotifier == null) {
- mPackageStatusNotifier = new TestPackageStatusNotifier();
- }
- return mPackageStatusNotifier;
- }
-
- @Override
- public DrawableLayerResolver getDrawableLayerResolver() {
- if (mDrawableLayerResolver == null) {
- mDrawableLayerResolver = new TestDrawableLayerResolver();
- }
- return mDrawableLayerResolver;
- }
-
- @Override
- public UserEventLogger getUserEventLogger(Context unused) {
- if (mUserEventLogger == null) {
- mUserEventLogger = new TestThemesUserEventLogger();
- }
- return mUserEventLogger;
- }
-
- @Override
- public KeyguardQuickAffordancePickerInteractor getKeyguardQuickAffordancePickerInteractor(
- Context context) {
- if (mKeyguardQuickAffordancePickerInteractor == null) {
- final CustomizationProviderClient client =
- new CustomizationProviderClientImpl(context, Dispatchers.getIO());
- mKeyguardQuickAffordancePickerInteractor = new KeyguardQuickAffordancePickerInteractor(
- new KeyguardQuickAffordancePickerRepository(client, Dispatchers.getIO()),
- client,
- () -> getKeyguardQuickAffordanceSnapshotRestorer(context));
- }
- return mKeyguardQuickAffordancePickerInteractor;
- }
-
- @Override
- public BaseFlags getFlags() {
- if (mFlags == null) {
- mFlags = new BaseFlags() {};
- }
-
- return mFlags;
- }
-
- @Override
- public Map<Integer, SnapshotRestorer> getSnapshotRestorers(Context context) {
- final Map<Integer, SnapshotRestorer> restorers = new HashMap<>();
- restorers.put(
- KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER,
- getKeyguardQuickAffordanceSnapshotRestorer(context));
- return restorers;
- }
-
- /** Returns the {@link CustomizationProviderClient}. */
- private CustomizationProviderClient getKeyguardQuickAffordancePickerProviderClient(
- Context context) {
- if (mCustomizationProviderClient == null) {
- mCustomizationProviderClient =
- new CustomizationProviderClientImpl(context, Dispatchers.getIO());
- }
-
- return mCustomizationProviderClient;
- }
-
- private KeyguardQuickAffordanceSnapshotRestorer getKeyguardQuickAffordanceSnapshotRestorer(
- Context context) {
- if (mKeyguardQuickAffordanceSnapshotRestorer == null) {
- mKeyguardQuickAffordanceSnapshotRestorer = new KeyguardQuickAffordanceSnapshotRestorer(
- getKeyguardQuickAffordancePickerInteractor(context),
- getKeyguardQuickAffordancePickerProviderClient(context));
- }
-
- return mKeyguardQuickAffordanceSnapshotRestorer;
- }
-
- private static final int KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = 1;
-}
diff --git a/tests/src/com/android/customization/testing/TestCustomizationInjector.kt b/tests/src/com/android/customization/testing/TestCustomizationInjector.kt
new file mode 100644
index 0000000..afc7131
--- /dev/null
+++ b/tests/src/com/android/customization/testing/TestCustomizationInjector.kt
@@ -0,0 +1,151 @@
+package com.android.customization.testing
+
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import android.view.LayoutInflater
+import androidx.fragment.app.FragmentActivity
+import com.android.customization.model.theme.OverlayManagerCompat
+import com.android.customization.model.theme.ThemeBundleProvider
+import com.android.customization.model.theme.ThemeManager
+import com.android.customization.module.CustomizationInjector
+import com.android.customization.module.CustomizationPreferences
+import com.android.customization.module.ThemesUserEventLogger
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.systemui.shared.clocks.DefaultClockProvider
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.module.DrawableLayerResolver
+import com.android.wallpaper.module.PackageStatusNotifier
+import com.android.wallpaper.module.UserEventLogger
+import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
+import com.android.wallpaper.testing.TestInjector
+import java.util.HashMap
+import kotlinx.coroutines.Dispatchers.IO
+
+/** Test implementation of the dependency injector. */
+class TestCustomizationInjector : TestInjector(), CustomizationInjector {
+ private var customizationPreferences: CustomizationPreferences? = null
+ private var themeManager: ThemeManager? = null
+ private var packageStatusNotifier: PackageStatusNotifier? = null
+ private var drawableLayerResolver: DrawableLayerResolver? = null
+ private var userEventLogger: UserEventLogger? = null
+ private var keyguardQuickAffordancePickerInteractor: KeyguardQuickAffordancePickerInteractor? =
+ null
+ private var flags: BaseFlags? = null
+ private var customizationProviderClient: CustomizationProviderClient? = null
+ private var keyguardQuickAffordanceSnapshotRestorer: KeyguardQuickAffordanceSnapshotRestorer? =
+ null
+ private var clockRegistry: ClockRegistry? = null
+ private var pluginManager: PluginManager? = null
+
+ override fun getCustomizationPreferences(context: Context): CustomizationPreferences {
+ return customizationPreferences
+ ?: TestDefaultCustomizationPreferences(context).also { customizationPreferences = it }
+ }
+
+ override fun getThemeManager(
+ provider: ThemeBundleProvider,
+ activity: FragmentActivity,
+ overlayManagerCompat: OverlayManagerCompat,
+ logger: ThemesUserEventLogger
+ ): ThemeManager {
+ return themeManager
+ ?: TestThemeManager(provider, activity, overlayManagerCompat, logger).also {
+ themeManager = it
+ }
+ }
+
+ override fun getPackageStatusNotifier(context: Context): PackageStatusNotifier {
+ return packageStatusNotifier
+ ?: TestPackageStatusNotifier().also {
+ packageStatusNotifier = TestPackageStatusNotifier()
+ }
+ }
+
+ override fun getDrawableLayerResolver(): DrawableLayerResolver {
+ return drawableLayerResolver
+ ?: TestDrawableLayerResolver().also { drawableLayerResolver = it }
+ }
+
+ override fun getUserEventLogger(context: Context): UserEventLogger {
+ return userEventLogger ?: TestThemesUserEventLogger().also { userEventLogger = it }
+ }
+
+ override fun getKeyguardQuickAffordancePickerInteractor(
+ context: Context
+ ): KeyguardQuickAffordancePickerInteractor {
+ return keyguardQuickAffordancePickerInteractor
+ ?: createCustomizationProviderClient(context).also {
+ keyguardQuickAffordancePickerInteractor = it
+ }
+ }
+
+ private fun createCustomizationProviderClient(
+ context: Context
+ ): KeyguardQuickAffordancePickerInteractor {
+ val client: CustomizationProviderClient = CustomizationProviderClientImpl(context, IO)
+ return KeyguardQuickAffordancePickerInteractor(
+ KeyguardQuickAffordancePickerRepository(client, IO),
+ client
+ ) { getKeyguardQuickAffordanceSnapshotRestorer(context) }
+ }
+
+ override fun getFlags(): BaseFlags {
+ return flags ?: object : BaseFlags() {}.also { flags = it }
+ }
+
+ override fun getSnapshotRestorers(context: Context): Map<Int, SnapshotRestorer> {
+ val restorers: MutableMap<Int, SnapshotRestorer> = HashMap()
+ restorers[KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER] =
+ getKeyguardQuickAffordanceSnapshotRestorer(context)
+ return restorers
+ }
+
+ /** Returns the [CustomizationProviderClient]. */
+ private fun getKeyguardQuickAffordancePickerProviderClient(
+ context: Context
+ ): CustomizationProviderClient {
+ return customizationProviderClient
+ ?: CustomizationProviderClientImpl(context, IO).also {
+ customizationProviderClient = it
+ }
+ }
+
+ private fun getKeyguardQuickAffordanceSnapshotRestorer(
+ context: Context
+ ): KeyguardQuickAffordanceSnapshotRestorer {
+ return keyguardQuickAffordanceSnapshotRestorer
+ ?: KeyguardQuickAffordanceSnapshotRestorer(
+ getKeyguardQuickAffordancePickerInteractor(context),
+ getKeyguardQuickAffordancePickerProviderClient(context)
+ )
+ .also { keyguardQuickAffordanceSnapshotRestorer = it }
+ }
+
+ override fun getClockRegistry(context: Context): ClockRegistry {
+ return clockRegistry
+ ?: ClockRegistry(
+ context,
+ getPluginManager(context),
+ Handler.getMain(),
+ isEnabled = true,
+ userHandle = UserHandle.USER_SYSTEM,
+ DefaultClockProvider(context, LayoutInflater.from(context), context.resources)
+ )
+ .also { clockRegistry = it }
+ }
+
+ override fun getPluginManager(context: Context): PluginManager {
+ return pluginManager ?: TestPluginManager().also { pluginManager = it }
+ }
+
+ companion object {
+ private const val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = 1
+ }
+}
diff --git a/tests/src/com/android/customization/testing/TestPluginManager.kt b/tests/src/com/android/customization/testing/TestPluginManager.kt
new file mode 100644
index 0000000..167d8dd
--- /dev/null
+++ b/tests/src/com/android/customization/testing/TestPluginManager.kt
@@ -0,0 +1,36 @@
+package com.android.customization.testing
+
+import com.android.systemui.plugins.Plugin
+import com.android.systemui.plugins.PluginListener
+import com.android.systemui.plugins.PluginManager
+
+class TestPluginManager : PluginManager {
+ override fun getPrivilegedPlugins(): Array<String> {
+ return emptyArray()
+ }
+
+ override fun <T : Plugin?> addPluginListener(listener: PluginListener<T>, cls: Class<T>) {}
+ override fun <T : Plugin?> addPluginListener(
+ listener: PluginListener<T>,
+ cls: Class<T>,
+ allowMultiple: Boolean
+ ) {}
+
+ override fun <T : Plugin?> addPluginListener(
+ action: String,
+ listener: PluginListener<T>,
+ cls: Class<T>
+ ) {}
+
+ override fun <T : Plugin?> addPluginListener(
+ action: String,
+ listener: PluginListener<T>,
+ cls: Class<T>,
+ allowMultiple: Boolean
+ ) {}
+
+ override fun removePluginListener(listener: PluginListener<*>?) {}
+ override fun <T> dependsOn(p: Plugin, cls: Class<T>): Boolean {
+ return false
+ }
+}