Add demo clock picker

Test: atest ClockCustomDemoFragmentTest
Bug: 242332371
Change-Id: I3e91e468f4630c11b8158c93d44c3e027ff6a221
diff --git a/robolectric_tests/src/com/android/customization/picker/clock/ClockCustomDemoFragmentTest.kt b/robolectric_tests/src/com/android/customization/picker/clock/ClockCustomDemoFragmentTest.kt
new file mode 100644
index 0000000..518b6a7
--- /dev/null
+++ b/robolectric_tests/src/com/android/customization/picker/clock/ClockCustomDemoFragmentTest.kt
@@ -0,0 +1,99 @@
+package com.android.customization.picker.clock
+
+import android.os.Handler
+import android.os.UserHandle
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.plugins.ClockId
+import com.android.systemui.plugins.ClockMetadata
+import com.android.systemui.plugins.ClockProviderPlugin
+import com.android.systemui.shared.clocks.ClockRegistry
+import com.android.systemui.shared.clocks.DefaultClockProvider
+import com.android.systemui.shared.plugins.PluginManager
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.robolectric.Robolectric
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/** Tests of [ClockCustomDemoFragment]. */
+@RunWith(RobolectricTestRunner::class)
+@Config(manifest = Config.NONE)
+class ClockCustomDemoFragmentTest {
+    private lateinit var mActivity: AppCompatActivity
+    private var mClockCustomDemoFragment: ClockCustomDemoFragment? = null
+    private lateinit var registry: ClockRegistry
+    @Mock private lateinit var mockPluginManager: PluginManager
+    @Mock private lateinit var mockHandler: Handler
+    @Mock private lateinit var fakePlugin: ClockProviderPlugin
+    @Mock private lateinit var defaultClockProvider: DefaultClockProvider
+
+    private var settingValue: String = ""
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mActivity = Robolectric.buildActivity(AppCompatActivity::class.java).get()
+        mClockCustomDemoFragment = ClockCustomDemoFragment()
+        Mockito.`when`(defaultClockProvider.getClocks())
+            .thenReturn(listOf(ClockMetadata("DEFAULT", "Default Clock")))
+        registry =
+            object :
+                ClockRegistry(
+                    mActivity,
+                    mockPluginManager,
+                    mockHandler,
+                    isEnabled = true,
+                    userHandle = UserHandle.USER_ALL,
+                    defaultClockProvider = defaultClockProvider
+                ) {
+                override var currentClockId: ClockId
+                    get() = settingValue
+                    set(value) {
+                        settingValue = value
+                    }
+
+                override fun getClocks(): List<ClockMetadata> {
+                    return defaultClockProvider.getClocks() +
+                        listOf(
+                            ClockMetadata("CLOCK_1", "Clock 1"),
+                            ClockMetadata("CLOCK_2", "Clock 2"),
+                            ClockMetadata("CLOCK_NOT_IN_USE", "Clock not in use")
+                        )
+                }
+            }
+
+        mClockCustomDemoFragment!!.clockRegistry = registry
+        mClockCustomDemoFragment!!.recyclerView = RecyclerView(mActivity)
+        mClockCustomDemoFragment!!.recyclerView.layoutManager =
+            LinearLayoutManager(mActivity, RecyclerView.VERTICAL, false)
+        mClockCustomDemoFragment!!.pluginListener.onPluginConnected(fakePlugin, mActivity)
+    }
+
+    @Test
+    fun testItemCount_getCorrectClockCount() {
+        Assert.assertEquals(3, mClockCustomDemoFragment!!.recyclerView.adapter!!.itemCount)
+    }
+
+    @Test
+    fun testClick_setCorrectClockId() {
+        mClockCustomDemoFragment!!
+            .recyclerView
+            .measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+        mClockCustomDemoFragment!!.recyclerView.layout(0, 0, 100, 10000)
+        val testPosition = 1
+        mClockCustomDemoFragment!!
+            .recyclerView
+            .findViewHolderForAdapterPosition(testPosition)
+            ?.itemView
+            ?.performClick()
+        Assert.assertEquals("CLOCK_1", settingValue)
+    }
+}
diff --git a/src/com/android/customization/model/clock/ClockSectionController.java b/src/com/android/customization/model/clock/ClockSectionController.java
index 285b5b7..545c1c4 100644
--- a/src/com/android/customization/model/clock/ClockSectionController.java
+++ b/src/com/android/customization/model/clock/ClockSectionController.java
@@ -20,7 +20,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.customization.picker.clock.ClockCustomFragment;
+import com.android.customization.picker.clock.ClockCustomDemoFragment;
 import com.android.customization.picker.clock.ClockSectionView;
 import com.android.wallpaper.R;
 import com.android.wallpaper.config.Flags;
@@ -45,7 +45,8 @@
         ClockSectionView view = (ClockSectionView) LayoutInflater.from(context).inflate(
                 R.layout.clock_section_view,
                 null);
-        view.setOnClickListener(v -> mNavigationController.navigateTo(new ClockCustomFragment()));
+        view.setOnClickListener(v ->
+                mNavigationController.navigateTo(new ClockCustomDemoFragment()));
         return view;
     }
 }
diff --git a/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt b/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt
new file mode 100644
index 0000000..f9c58ab
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ClockCustomDemoFragment.kt
@@ -0,0 +1,191 @@
+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.view.ContextThemeWrapper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.core.view.setPadding
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+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.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.PluginManager
+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.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
+    val pluginListener =
+        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)
+            }
+        }
+
+    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
+            )
+        pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java, true)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        val view = inflater.inflate(R.layout.fragment_clock_custom_picker_demo, container, false)
+        setUpToolbar(view)
+        return view
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        recyclerView = view.requireViewById(R.id.clock_preview_card_list_demo)
+        recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
+        super.onViewCreated(view, savedInstanceState)
+    }
+
+    override fun getDefaultTitle(): CharSequence {
+        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
+    ) : RecyclerView.Adapter<ClockRecyclerAdapter.ViewHolder>() {
+        class ViewHolder(val view: View, val textView: TextView, val onItemClicked: (Int) -> Unit) :
+            RecyclerView.ViewHolder(view) {
+            init {
+                itemView.setOnClickListener { onItemClicked(absoluteAdapterPosition) }
+            }
+        }
+
+        override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
+            val rootView = FrameLayout(viewGroup.context)
+            val textView =
+                TextView(ContextThemeWrapper(viewGroup.context, R.style.SectionTitleTextStyle))
+            textView.setPadding(ITEM_PADDING)
+            rootView.addView(textView)
+            val lp = RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
+            rootView.setLayoutParams(lp)
+            return ViewHolder(
+                rootView,
+                textView,
+                { clockRegistry.currentClockId = list[it].clockId }
+            )
+        }
+
+        override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
+            viewHolder.textView.text = list[position].name
+        }
+
+        override fun getItemCount() = list.size
+
+        companion object {
+            val ITEM_PADDING = 40
+        }
+    }
+}