Merge "Refactor StatsLogUserEventLogger" into udc-qpr-dev
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bff2d76
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.iml
diff --git a/res/values/config.xml b/res/values/config.xml
index d992bf3..ad81e67 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,4 +19,8 @@
<item type="id" name="option_tile" />
<!-- ID for the label of an option tile -->
<item type="id" name="option_label" />
+
+ <!-- ID for the a11y actions on carousel -->
+ <item type="id" name="action_scroll_forward" />
+ <item type="id" name="action_scroll_backward" />
</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b7574a3..680947d 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -507,4 +507,22 @@
[CHAR LIMIT=NONE].
-->
<string name="content_description_color_option">Color option <xliff:g name="color_number" example="1">%1$d</xliff:g></string>
+
+ <!--
+ Accessibility label for forward scrolling in the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="scroll_forward_and_select">Swipe left to choose a different clock face</string>
+
+ <!--
+ Accessibility label for backward scrolling in the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="scroll_backward_and_select">Swipe right to choose a different clock face</string>
+
+ <!--
+ Accessibility label for the carousel of clock faces.
+ [CHAR LIMIT=128].
+ -->
+ <string name="custom_clocks_label">Custom Clocks</string>
</resources>
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
index be6c6cb..2d22c38 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -33,6 +33,7 @@
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -146,9 +147,10 @@
)
.map { setting -> setting == 1 }
.map { isDynamic -> if (isDynamic) ClockSize.DYNAMIC else ClockSize.SMALL }
+ .distinctUntilChanged()
.shareIn(
scope = scope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
replay = 1,
)
diff --git a/src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.kt b/src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.kt
new file mode 100644
index 0000000..5e3c26e
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/binder/CarouselAccessibilityDelegate.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
+import com.android.wallpaper.R
+
+class CarouselAccessibilityDelegate(
+ private val context: Context,
+ private val scrollForwardCallback: () -> Unit,
+ private val scrollBackwardCallback: () -> Unit
+) : View.AccessibilityDelegate() {
+
+ var contentDescriptionOfSelectedClock = ""
+
+ private val ACTION_SCROLL_BACKWARD = R.id.action_scroll_backward
+ private val ACTION_SCROLL_FORWARD = R.id.action_scroll_forward
+
+ override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info?.isScrollable = true
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ ACTION_SCROLL_FORWARD,
+ context.getString(R.string.scroll_forward_and_select)
+ )
+ )
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ ACTION_SCROLL_BACKWARD,
+ context.getString(R.string.scroll_backward_and_select)
+ )
+ )
+ info?.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS)
+ // We need to specifically set the content description since for some reason the talkback
+ // service does not go to children of the clock carousel in the view hierarchy
+ info?.contentDescription = contentDescriptionOfSelectedClock
+ }
+
+ override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+ when (action) {
+ ACTION_SCROLL_BACKWARD -> {
+ scrollBackwardCallback.invoke()
+ return true
+ }
+ ACTION_SCROLL_FORWARD -> {
+ scrollForwardCallback.invoke()
+ return true
+ }
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+}
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 13c80c8..89fac89 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -15,6 +15,7 @@
*/
package com.android.customization.picker.clock.ui.binder
+import android.content.Context
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isVisible
@@ -27,6 +28,7 @@
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
import com.android.wallpaper.R
+import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -34,8 +36,10 @@
@JvmStatic
fun bind(
+ context: Context,
carouselView: ClockCarouselView,
singleClockView: ViewGroup,
+ screenPreviewClickView: ScreenPreviewClickView,
viewModel: ClockCarouselViewModel,
clockViewFactory: ClockViewFactory,
lifecycleOwner: LifecycleOwner,
@@ -43,6 +47,20 @@
) {
carouselView.setClockViewFactory(clockViewFactory)
clockViewFactory.updateRegionDarkness()
+ val carouselAccessibilityDelegate =
+ CarouselAccessibilityDelegate(
+ context,
+ scrollForwardCallback = {
+ // Callback code for scrolling forward
+ carouselView.transitionToNext()
+ },
+ scrollBackwardCallback = {
+ // Callback code for scrolling backward
+ carouselView.transitionToPrevious()
+ }
+ )
+ screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
+
val singleClockHostView =
singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
lifecycleOwner.lifecycleScope.launch {
@@ -71,6 +89,8 @@
launch {
viewModel.selectedIndex.collect { selectedIndex ->
+ carouselAccessibilityDelegate.contentDescriptionOfSelectedClock =
+ carouselView.getContentDescription(selectedIndex)
carouselView.setSelectedClockIndex(selectedIndex)
}
}
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
deleted file mode 100644
index f138d6a..0000000
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.android.customization.picker.clock.ui.fragment
-
-import android.content.Context
-import android.os.Bundle
-import android.util.TypedValue
-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 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.shared.clocks.ClockRegistry
-import com.android.wallpaper.R
-import com.android.wallpaper.module.InjectorProvider
-import com.android.wallpaper.picker.AppbarFragment
-
-class ClockCustomDemoFragment : AppbarFragment() {
- @VisibleForTesting lateinit var recyclerView: RecyclerView
- @VisibleForTesting lateinit var clockRegistry: ClockRegistry
-
- 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)
- clockRegistry =
- (InjectorProvider.getInjector() as ThemePickerInjector).getClockRegistry(
- requireContext(),
- )
- val listInUse = clockRegistry.getClocks().filter { "NOT_IN_USE" !in it.clockId }
-
- recyclerView = view.requireViewById(R.id.clock_preview_card_list_demo)
- recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
- recyclerView.adapter =
- ClockRecyclerAdapter(listInUse, requireContext()) {
- clockRegistry.currentClockId = it.clockId
- Toast.makeText(context, "${it.name} selected", Toast.LENGTH_SHORT).show()
- }
- return view
- }
-
- override fun getDefaultTitle(): CharSequence {
- return getString(R.string.clock_title)
- }
-
- internal class ClockRecyclerAdapter(
- val list: List<ClockMetadata>,
- val context: Context,
- val onClockSelected: (ClockMetadata) -> Unit
- ) : 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 outValue = TypedValue()
- context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
- rootView.setBackgroundResource(outValue.resourceId)
- val lp = RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
- rootView.layoutParams = lp
- return ViewHolder(rootView, textView) { onClockSelected(list[it]) }
- }
-
- override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
- viewHolder.textView.text = list[position].name
- }
-
- override fun getItemCount() = list.size
-
- companion object {
- val ITEM_PADDING = 40
- }
- }
-}
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
index 73d5508..8764e54 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -16,6 +16,7 @@
package com.android.customization.picker.clock.ui.view
import android.content.Context
+import android.content.res.Resources
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
@@ -60,6 +61,7 @@
val clockCarousel = LayoutInflater.from(context).inflate(R.layout.clock_carousel, this)
carousel = clockCarousel.requireViewById(R.id.carousel)
motionLayout = clockCarousel.requireViewById(R.id.motion_container)
+ motionLayout.contentDescription = context.getString(R.string.custom_clocks_label)
}
/**
@@ -69,6 +71,24 @@
clockViewFactory = factory
}
+ fun transitionToNext() {
+ val index = (carousel.currentIndex + 1) % carousel.count
+ if (index < carousel.count && index > 0) {
+ carousel.transitionToIndex(index, 0)
+ }
+ }
+
+ fun transitionToPrevious() {
+ val index = (carousel.currentIndex - 1) % carousel.count
+ if (index < carousel.count && index > 0) {
+ carousel.transitionToIndex(index, 0)
+ }
+ }
+
+ fun getContentDescription(index: Int): String {
+ return adapter.getContentDescription(index, resources)
+ }
+
fun setUpClockCarouselView(
clockSize: ClockSize,
clocks: List<ClockCarouselItemViewModel>,
@@ -304,6 +324,10 @@
private val onClockSelected: (clock: ClockCarouselItemViewModel) -> Unit
) : Carousel.Adapter {
+ fun getContentDescription(index: Int, resources: Resources): String {
+ return clocks[index].getContentDescription(resources)
+ }
+
override fun count(): Int {
return clocks.size
}
@@ -336,7 +360,7 @@
val isMiddleView = isMiddleView(viewRoot.id)
// Accessibility
- viewRoot.contentDescription = clocks[index].getContentDescription(view.resources)
+ viewRoot.contentDescription = getContentDescription(index, view.resources)
viewRoot.isSelected = isMiddleView
when (clockSize) {
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 4322009..8e91798 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -46,6 +46,7 @@
import com.android.wallpaper.module.CurrentWallpaperInfoFactory
import com.android.wallpaper.module.CustomizationSections
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
+import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewView
import com.android.wallpaper.util.DisplayUtils
@@ -100,6 +101,8 @@
override fun createView(context: Context): ScreenPreviewView {
val view = super.createView(context)
if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
+ val screenPreviewClickView: ScreenPreviewClickView =
+ view.findViewById(R.id.screen_preview_click_view)
val clockColorAndSizeButtonStub: ViewStub =
view.requireViewById(R.id.clock_color_and_size_button)
clockColorAndSizeButtonStub.layoutResource = R.layout.clock_color_and_size_button
@@ -162,8 +165,10 @@
bindJob =
lifecycleOwner.lifecycleScope.launch {
ClockCarouselViewBinder.bind(
+ context = context,
carouselView = carouselView,
singleClockView = singleClockView,
+ screenPreviewClickView = screenPreviewClickView,
viewModel = viewModel,
clockViewFactory = clockViewFactory,
lifecycleOwner = lifecycleOwner,
diff --git a/tests/robotests/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragmentTest.kt b/tests/robotests/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragmentTest.kt
deleted file mode 100644
index 0a54312..0000000
--- a/tests/robotests/src/com/android/customization/picker/clock/ui/fragment/ClockCustomDemoFragmentTest.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.android.customization.picker.clock.ui.fragment
-
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.plugins.ClockMetadata
-import com.android.systemui.plugins.ClockSettings
-import com.android.systemui.plugins.PluginManager
-import com.android.systemui.shared.clocks.ClockRegistry
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-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)
-@Ignore("b/270606895")
-class ClockCustomDemoFragmentTest {
- private lateinit var mActivity: AppCompatActivity
- private var mClockCustomDemoFragment: ClockCustomDemoFragment? = null
- @Mock private lateinit var registry: ClockRegistry
- @Mock private lateinit var mockPluginManager: PluginManager
-
- private var settingValue: ClockSettings? = null
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- mActivity = Robolectric.buildActivity(AppCompatActivity::class.java).get()
- mClockCustomDemoFragment = ClockCustomDemoFragment()
- whenever(registry.getClocks())
- .thenReturn(
- 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)
- }
-
- @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()
- verify(registry).currentClockId = "CLOCK_1"
- }
-}
diff --git a/tests/src/com/android/customization/testing/TestDefaultCustomizationPreferences.java b/tests/src/com/android/customization/testing/TestDefaultCustomizationPreferences.java
index bcf5a5f..81890f0 100644
--- a/tests/src/com/android/customization/testing/TestDefaultCustomizationPreferences.java
+++ b/tests/src/com/android/customization/testing/TestDefaultCustomizationPreferences.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 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.testing;
import android.content.Context;
diff --git a/tests/src/com/android/customization/testing/TestDrawableLayerResolver.java b/tests/src/com/android/customization/testing/TestDrawableLayerResolver.java
index e507221..8b16299 100644
--- a/tests/src/com/android/customization/testing/TestDrawableLayerResolver.java
+++ b/tests/src/com/android/customization/testing/TestDrawableLayerResolver.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 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.testing;
import android.graphics.drawable.Drawable;
diff --git a/tests/src/com/android/customization/testing/TestPackageStatusNotifier.java b/tests/src/com/android/customization/testing/TestPackageStatusNotifier.java
index 1e6a1a2..2aadae8 100644
--- a/tests/src/com/android/customization/testing/TestPackageStatusNotifier.java
+++ b/tests/src/com/android/customization/testing/TestPackageStatusNotifier.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 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.testing;
import com.android.wallpaper.module.PackageStatusNotifier;
diff --git a/tests/src/com/android/customization/testing/TestThemeManager.java b/tests/src/com/android/customization/testing/TestThemeManager.java
index c4d25fb..5175b24 100644
--- a/tests/src/com/android/customization/testing/TestThemeManager.java
+++ b/tests/src/com/android/customization/testing/TestThemeManager.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 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.testing;
import androidx.fragment.app.FragmentActivity;
diff --git a/tests/src/com/android/customization/testing/TestThemesUserEventLogger.java b/tests/src/com/android/customization/testing/TestThemesUserEventLogger.java
index 2bb2082..22a5b94 100644
--- a/tests/src/com/android/customization/testing/TestThemesUserEventLogger.java
+++ b/tests/src/com/android/customization/testing/TestThemesUserEventLogger.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 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.testing;
import com.android.customization.model.color.ColorOption;