diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
new file mode 100644
index 0000000000000..ca0a091e65bbe
--- /dev/null
+++ b/tests/SilkFX/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2010 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.
+//
+
+android_test {
+ name: "SilkFX",
+ srcs: ["**/*.java", "**/*.kt"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
new file mode 100644
index 0000000000000..ca9550a9eeab0
--- /dev/null
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/SilkFX/res/drawable-nodpi/dark_notification.png b/tests/SilkFX/res/drawable-nodpi/dark_notification.png
new file mode 100644
index 0000000000000..6de6c2ae785c1
Binary files /dev/null and b/tests/SilkFX/res/drawable-nodpi/dark_notification.png differ
diff --git a/tests/SilkFX/res/drawable-nodpi/light_notification.png b/tests/SilkFX/res/drawable-nodpi/light_notification.png
new file mode 100644
index 0000000000000..81a67cd3d3883
Binary files /dev/null and b/tests/SilkFX/res/drawable-nodpi/light_notification.png differ
diff --git a/tests/SilkFX/res/layout/bling_notifications.xml b/tests/SilkFX/res/layout/bling_notifications.xml
new file mode 100644
index 0000000000000..6d266b701a687
--- /dev/null
+++ b/tests/SilkFX/res/layout/bling_notifications.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/color_mode_controls.xml b/tests/SilkFX/res/layout/color_mode_controls.xml
new file mode 100644
index 0000000000000..c0c0bab8a6057
--- /dev/null
+++ b/tests/SilkFX/res/layout/color_mode_controls.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/common_base.xml b/tests/SilkFX/res/layout/common_base.xml
new file mode 100644
index 0000000000000..944c6846fbf74
--- /dev/null
+++ b/tests/SilkFX/res/layout/common_base.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/SilkFX/res/layout/hdr_glows.xml b/tests/SilkFX/res/layout/hdr_glows.xml
new file mode 100644
index 0000000000000..b6050645866a8
--- /dev/null
+++ b/tests/SilkFX/res/layout/hdr_glows.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
new file mode 100644
index 0000000000000..76e62a6c8cff0
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.silkfx
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseExpandableListAdapter
+import android.widget.ExpandableListView
+import android.widget.TextView
+import com.android.test.silkfx.app.CommonDemoActivity
+import com.android.test.silkfx.app.EXTRA_LAYOUT
+import com.android.test.silkfx.app.EXTRA_TITLE
+import com.android.test.silkfx.hdr.GlowActivity
+import kotlin.reflect.KClass
+
+class Demo(val name: String, val makeIntent: (Context) -> Intent) {
+ constructor(name: String, activity: KClass) : this(name, { context ->
+ Intent(context, activity.java)
+ })
+ constructor(name: String, layout: Int) : this(name, { context ->
+ Intent(context, CommonDemoActivity::class.java).apply {
+ putExtra(EXTRA_LAYOUT, layout)
+ putExtra(EXTRA_TITLE, name)
+ }
+ })
+}
+data class DemoGroup(val groupName: String, val demos: List)
+
+private val AllDemos = listOf(
+ DemoGroup("HDR", listOf(
+ Demo("Glow", GlowActivity::class),
+ Demo("Blingy Notifications", R.layout.bling_notifications)
+ ))
+)
+
+class Main : Activity() {
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val list = ExpandableListView(this)
+
+ setContentView(list)
+
+ val inflater = LayoutInflater.from(this)
+ list.setAdapter(object : BaseExpandableListAdapter() {
+ override fun getGroup(groupPosition: Int): DemoGroup {
+ return AllDemos[groupPosition]
+ }
+
+ override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean = true
+
+ override fun hasStableIds(): Boolean = true
+
+ override fun getGroupView(
+ groupPosition: Int,
+ isExpanded: Boolean,
+ convertView: View?,
+ parent: ViewGroup?
+ ): View {
+ val view = (convertView ?: inflater.inflate(
+ android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView
+ view.text = AllDemos[groupPosition].groupName
+ return view
+ }
+
+ override fun getChildrenCount(groupPosition: Int): Int {
+ return AllDemos[groupPosition].demos.size
+ }
+
+ override fun getChild(groupPosition: Int, childPosition: Int): Demo {
+ return AllDemos[groupPosition].demos[childPosition]
+ }
+
+ override fun getGroupId(groupPosition: Int): Long = groupPosition.toLong()
+
+ override fun getChildView(
+ groupPosition: Int,
+ childPosition: Int,
+ isLastChild: Boolean,
+ convertView: View?,
+ parent: ViewGroup?
+ ): View {
+ val view = (convertView ?: inflater.inflate(
+ android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView
+ view.text = AllDemos[groupPosition].demos[childPosition].name
+ return view
+ }
+
+ override fun getChildId(groupPosition: Int, childPosition: Int): Long {
+ return (groupPosition.toLong() shl 32) or childPosition.toLong()
+ }
+
+ override fun getGroupCount(): Int {
+ return AllDemos.size
+ }
+ })
+
+ list.setOnChildClickListener { _, _, groupPosition, childPosition, _ ->
+ val demo = AllDemos[groupPosition].demos[childPosition]
+ startActivity(demo.makeIntent(this))
+ return@setOnChildClickListener true
+ }
+
+ AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
+ }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
new file mode 100644
index 0000000000000..89011b51b8d67
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.silkfx.app
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+
+open class BaseDemoActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val inflater = LayoutInflater.from(this)
+ inflater.factory2 = object : LayoutInflater.Factory2 {
+ private val sClassPrefixList = arrayOf(
+ "android.widget.",
+ "android.webkit.",
+ "android.app.",
+ null
+ )
+ override fun onCreateView(
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? {
+ return onCreateView(name, context, attrs)
+ }
+
+ override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
+ for (prefix in sClassPrefixList) {
+ try {
+ val view = inflater.createView(name, prefix, attrs)
+ if (view != null) {
+ if (view is WindowObserver) {
+ view.setWindow(window)
+ }
+ return view
+ }
+ } catch (e: ClassNotFoundException) { }
+ }
+ return null
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ actionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ onBackPressed()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
new file mode 100644
index 0000000000000..e0a0a20bc0a0c
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.silkfx.app
+
+import com.android.test.silkfx.R
+import android.os.Bundle
+import android.view.LayoutInflater
+
+const val EXTRA_LAYOUT = "layout"
+const val EXTRA_TITLE = "title"
+
+class CommonDemoActivity : BaseDemoActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val extras = intent.extras ?: return finish()
+
+ val layout = extras.getInt(EXTRA_LAYOUT, -1)
+ if (layout == -1) {
+ finish()
+ return
+ }
+ val title = extras.getString(EXTRA_TITLE, "SilkFX")
+ window.setTitle(title)
+
+ setContentView(R.layout.common_base)
+ actionBar?.title = title
+ LayoutInflater.from(this).inflate(layout, findViewById(R.id.demo_container), true)
+ }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
new file mode 100644
index 0000000000000..3d989a54cf276
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.silkfx.app
+
+import android.view.Window
+
+interface WindowObserver {
+ fun setWindow(window: Window)
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
new file mode 100644
index 0000000000000..4b85953a24b93
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.silkfx.common
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.util.AttributeSet
+import android.view.View
+
+open class BaseDrawingView : View {
+ val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
+ val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020)
+ val lab = ColorSpace.get(ColorSpace.Named.CIE_LAB)
+
+ val density: Float
+ val dp: Int.() -> Float
+
+ fun color(red: Float, green: Float, blue: Float, alpha: Float = 1f): Long {
+ return Color.pack(red, green, blue, alpha, scRGB)
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ setWillNotDraw(false)
+ isClickable = true
+ density = resources.displayMetrics.density
+ dp = { this * density }
+ }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
new file mode 100644
index 0000000000000..c3d689c5209d3
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.silkfx.common
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.hardware.display.DisplayManager
+import android.util.AttributeSet
+import android.view.Window
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.test.silkfx.app.WindowObserver
+import java.lang.Exception
+
+class ColorModeControls : LinearLayout, WindowObserver {
+ private val COLOR_MODE_HDR10 = 3
+
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ displayManager = context.getSystemService(DisplayManager::class.java)!!
+ }
+
+ private var window: Window? = null
+ private var currentMode: TextView? = null
+ private val displayManager: DisplayManager
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ val window = window ?: throw IllegalStateException("Failed to attach window")
+
+ currentMode = findViewById(R.id.current_mode)!!
+ setColorMode(window.colorMode)
+
+ findViewById