Loading packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +1 −1 Original line number Diff line number Diff line Loading @@ -189,9 +189,9 @@ open class ControlsProviderSelectorActivity @Inject constructor( authorizedPanelsRepository.addAuthorizedPanels( setOf(serviceInfo.componentName.packageName) ) animateExitAndFinish() val selected = SelectedItem.PanelItem(appName, componentName) controlsController.setPreferredSelection(selected) animateExitAndFinish() openControlsOrigin() } dialog = null Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +84 −30 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control Loading @@ -38,6 +39,7 @@ import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.BaseAdapter import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout Loading Loading @@ -90,6 +92,7 @@ private data class ControlKey(val componentName: ComponentName, val controlId: S class ControlsUiControllerImpl @Inject constructor ( val controlsController: Lazy<ControlsController>, val context: Context, private val packageManager: PackageManager, @Main val uiExecutor: DelayableExecutor, @Background val bgExecutor: DelayableExecutor, val controlsListingController: Lazy<ControlsListingController>, Loading @@ -113,6 +116,11 @@ class ControlsUiControllerImpl @Inject constructor ( private const val PREF_IS_PANEL = "controls_is_panel" private const val FADE_IN_MILLIS = 200L private const val OPEN_APP_ID = 0L private const val ADD_CONTROLS_ID = 1L private const val ADD_APP_ID = 2L private const val EDIT_CONTROLS_ID = 3L } private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION Loading Loading @@ -140,6 +148,9 @@ class ControlsUiControllerImpl @Inject constructor ( it.getTitle() } private var openAppIntent: Intent? = null private var overflowMenuAdapter: BaseAdapter? = null private val onSeedingComplete = Consumer<Boolean> { accepted -> if (accepted) { Loading Loading @@ -216,6 +227,8 @@ class ControlsUiControllerImpl @Inject constructor ( this.parent = parent this.onDismiss = onDismiss this.activityContext = activityContext this.openAppIntent = null this.overflowMenuAdapter = null hidden = false retainCache = false Loading Loading @@ -306,6 +319,12 @@ class ControlsUiControllerImpl @Inject constructor ( startTargetedActivity(si, ControlsEditingActivity::class.java) } private fun startDefaultActivity() { openAppIntent?.let { startActivity(it, animateExtra = false) } } private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) { val i = Intent(activityContext, klazz) putIntentExtras(i, si) Loading @@ -329,9 +348,11 @@ class ControlsUiControllerImpl @Inject constructor ( startActivity(i) } private fun startActivity(intent: Intent) { private fun startActivity(intent: Intent, animateExtra: Boolean = true) { // Force animations when transitioning from a dialog to an activity if (animateExtra) { intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) } if (keyguardStateController.isShowing()) { activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */) Loading Loading @@ -383,8 +404,31 @@ class ControlsUiControllerImpl @Inject constructor ( Log.w(ControlsUiController.TAG, "Not TaskViewFactory to display panel $selectionItem") } bgExecutor.execute { val intent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER) .setPackage(selectionItem.componentName.packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) val intents = packageManager .queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0L)) intents.firstOrNull { it.activityInfo.exported }?.let { resolved -> intent.setPackage(null) intent.setComponent(resolved.activityInfo.componentName) openAppIntent = intent parent.post { // This will call show on the PopupWindow in the same thread, so make sure this // happens in the view thread. overflowMenuAdapter?.notifyDataSetChanged() } } } createDropDown(panelsAndStructures, selectionItem) createMenu() val currentApps = panelsAndStructures.map { it.componentName }.toSet() val allApps = controlsListingController.get() .getCurrentServices().map { it.componentName }.toSet() createMenu(extraApps = (allApps - currentApps).isNotEmpty()) } private fun createPanelView(componentName: ComponentName) { Loading Loading @@ -423,28 +467,41 @@ class ControlsUiControllerImpl @Inject constructor ( } } private fun createMenu() { private fun createMenu(extraApps: Boolean) { val isPanel = selectedItem is SelectedItem.PanelItem val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure ?: EMPTY_STRUCTURE val newFlows = featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS) val addControlsId = if (newFlows || isPanel) { R.string.controls_menu_add_another_app val items = buildList { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_open_app), OPEN_APP_ID )) if (newFlows || isPanel) { if (extraApps) { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add_another_app), ADD_APP_ID )) } } else { R.string.controls_menu_add add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add), ADD_CONTROLS_ID )) } if (!isPanel) { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_edit), EDIT_CONTROLS_ID )) } } val items = if (isPanel) { arrayOf( context.resources.getString(addControlsId), ) } else { arrayOf( context.resources.getString(addControlsId), context.resources.getString(R.string.controls_menu_edit) ) val adapter = OverflowMenuAdapter(context, R.layout.controls_more_item, items) { position -> getItemId(position) != OPEN_APP_ID || openAppIntent != null } var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items) val anchor = parent.requireViewById<ImageView>(R.id.controls_more) anchor.setOnClickListener(object : View.OnClickListener { Loading @@ -462,25 +519,21 @@ class ControlsUiControllerImpl @Inject constructor ( pos: Int, id: Long ) { when (pos) { // 0: Add Control 0 -> { if (isPanel || newFlows) { startProviderSelectorActivity() } else { startFavoritingActivity(selectedStructure) } } // 1: Edit controls 1 -> startEditingActivity(selectedStructure) when (id) { OPEN_APP_ID -> startDefaultActivity() ADD_APP_ID -> startProviderSelectorActivity() ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure) EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure) } dismiss() } }) show() listView?.post { listView?.requestAccessibilityFocus() } } } }) overflowMenuAdapter = adapter } private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) { Loading Loading @@ -542,6 +595,7 @@ class ControlsUiControllerImpl @Inject constructor ( } }) show() listView?.post { listView?.requestAccessibilityFocus() } } } }) Loading Loading @@ -631,7 +685,7 @@ class ControlsUiControllerImpl @Inject constructor ( .putString(PREF_COMPONENT, selectedItem.componentName.flattenToString()) .putString(PREF_STRUCTURE_OR_APP_NAME, selectedItem.name.toString()) .putBoolean(PREF_IS_PANEL, selectedItem is SelectedItem.PanelItem) .commit() .apply() } private fun maybeUpdateSelectedItem(item: SelectionItem): Boolean { Loading packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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.systemui.controls.ui import android.content.Context import android.widget.ArrayAdapter import androidx.annotation.LayoutRes open class OverflowMenuAdapter( context: Context, @LayoutRes layoutId: Int, itemsWithIds: List<MenuItem>, private val isEnabledInternal: OverflowMenuAdapter.(Int) -> Boolean ) : ArrayAdapter<CharSequence>(context, layoutId, itemsWithIds.map(MenuItem::text)) { private val ids = itemsWithIds.map(MenuItem::id) override fun getItemId(position: Int): Long { return ids[position] } override fun isEnabled(position: Int): Boolean { return isEnabledInternal(position) } data class MenuItem(val text: CharSequence, val id: Long) } packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ServiceInfo import android.os.UserHandle import android.service.controls.ControlsProviderService Loading Loading @@ -95,6 +96,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() { @Mock lateinit var dumpManager: DumpManager @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository @Mock lateinit var featureFlags: FeatureFlags @Mock lateinit var packageManager: PackageManager val sharedPreferences = FakeSharedPreferences() lateinit var controlsSettingsRepository: FakeControlsSettingsRepository Loading Loading @@ -124,6 +126,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() { ControlsUiControllerImpl( Lazy { controlsController }, context, packageManager, uiExecutor, bgExecutor, Lazy { controlsListingController }, Loading packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * 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.systemui.controls.ui import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class OverflowMenuAdapterTest : SysuiTestCase() { @Test fun testGetItemId() { val ids = listOf(27L, 73L) val labels = listOf("first", "second") val adapter = OverflowMenuAdapter( context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } ) { true } ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) } } @Test fun testCheckEnabled() { val ids = listOf(27L, 73L) val labels = listOf("first", "second") val adapter = OverflowMenuAdapter( context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } ) { position -> position == 0 } assertThat(adapter.isEnabled(0)).isTrue() assertThat(adapter.isEnabled(1)).isFalse() } } Loading
packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +1 −1 Original line number Diff line number Diff line Loading @@ -189,9 +189,9 @@ open class ControlsProviderSelectorActivity @Inject constructor( authorizedPanelsRepository.addAuthorizedPanels( setOf(serviceInfo.componentName.packageName) ) animateExitAndFinish() val selected = SelectedItem.PanelItem(appName, componentName) controlsController.setPreferredSelection(selected) animateExitAndFinish() openControlsOrigin() } dialog = null Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +84 −30 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control Loading @@ -38,6 +39,7 @@ import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.BaseAdapter import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout Loading Loading @@ -90,6 +92,7 @@ private data class ControlKey(val componentName: ComponentName, val controlId: S class ControlsUiControllerImpl @Inject constructor ( val controlsController: Lazy<ControlsController>, val context: Context, private val packageManager: PackageManager, @Main val uiExecutor: DelayableExecutor, @Background val bgExecutor: DelayableExecutor, val controlsListingController: Lazy<ControlsListingController>, Loading @@ -113,6 +116,11 @@ class ControlsUiControllerImpl @Inject constructor ( private const val PREF_IS_PANEL = "controls_is_panel" private const val FADE_IN_MILLIS = 200L private const val OPEN_APP_ID = 0L private const val ADD_CONTROLS_ID = 1L private const val ADD_APP_ID = 2L private const val EDIT_CONTROLS_ID = 3L } private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION Loading Loading @@ -140,6 +148,9 @@ class ControlsUiControllerImpl @Inject constructor ( it.getTitle() } private var openAppIntent: Intent? = null private var overflowMenuAdapter: BaseAdapter? = null private val onSeedingComplete = Consumer<Boolean> { accepted -> if (accepted) { Loading Loading @@ -216,6 +227,8 @@ class ControlsUiControllerImpl @Inject constructor ( this.parent = parent this.onDismiss = onDismiss this.activityContext = activityContext this.openAppIntent = null this.overflowMenuAdapter = null hidden = false retainCache = false Loading Loading @@ -306,6 +319,12 @@ class ControlsUiControllerImpl @Inject constructor ( startTargetedActivity(si, ControlsEditingActivity::class.java) } private fun startDefaultActivity() { openAppIntent?.let { startActivity(it, animateExtra = false) } } private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) { val i = Intent(activityContext, klazz) putIntentExtras(i, si) Loading @@ -329,9 +348,11 @@ class ControlsUiControllerImpl @Inject constructor ( startActivity(i) } private fun startActivity(intent: Intent) { private fun startActivity(intent: Intent, animateExtra: Boolean = true) { // Force animations when transitioning from a dialog to an activity if (animateExtra) { intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) } if (keyguardStateController.isShowing()) { activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */) Loading Loading @@ -383,8 +404,31 @@ class ControlsUiControllerImpl @Inject constructor ( Log.w(ControlsUiController.TAG, "Not TaskViewFactory to display panel $selectionItem") } bgExecutor.execute { val intent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER) .setPackage(selectionItem.componentName.packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) val intents = packageManager .queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(0L)) intents.firstOrNull { it.activityInfo.exported }?.let { resolved -> intent.setPackage(null) intent.setComponent(resolved.activityInfo.componentName) openAppIntent = intent parent.post { // This will call show on the PopupWindow in the same thread, so make sure this // happens in the view thread. overflowMenuAdapter?.notifyDataSetChanged() } } } createDropDown(panelsAndStructures, selectionItem) createMenu() val currentApps = panelsAndStructures.map { it.componentName }.toSet() val allApps = controlsListingController.get() .getCurrentServices().map { it.componentName }.toSet() createMenu(extraApps = (allApps - currentApps).isNotEmpty()) } private fun createPanelView(componentName: ComponentName) { Loading Loading @@ -423,28 +467,41 @@ class ControlsUiControllerImpl @Inject constructor ( } } private fun createMenu() { private fun createMenu(extraApps: Boolean) { val isPanel = selectedItem is SelectedItem.PanelItem val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure ?: EMPTY_STRUCTURE val newFlows = featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS) val addControlsId = if (newFlows || isPanel) { R.string.controls_menu_add_another_app val items = buildList { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_open_app), OPEN_APP_ID )) if (newFlows || isPanel) { if (extraApps) { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add_another_app), ADD_APP_ID )) } } else { R.string.controls_menu_add add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add), ADD_CONTROLS_ID )) } if (!isPanel) { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_edit), EDIT_CONTROLS_ID )) } } val items = if (isPanel) { arrayOf( context.resources.getString(addControlsId), ) } else { arrayOf( context.resources.getString(addControlsId), context.resources.getString(R.string.controls_menu_edit) ) val adapter = OverflowMenuAdapter(context, R.layout.controls_more_item, items) { position -> getItemId(position) != OPEN_APP_ID || openAppIntent != null } var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items) val anchor = parent.requireViewById<ImageView>(R.id.controls_more) anchor.setOnClickListener(object : View.OnClickListener { Loading @@ -462,25 +519,21 @@ class ControlsUiControllerImpl @Inject constructor ( pos: Int, id: Long ) { when (pos) { // 0: Add Control 0 -> { if (isPanel || newFlows) { startProviderSelectorActivity() } else { startFavoritingActivity(selectedStructure) } } // 1: Edit controls 1 -> startEditingActivity(selectedStructure) when (id) { OPEN_APP_ID -> startDefaultActivity() ADD_APP_ID -> startProviderSelectorActivity() ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure) EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure) } dismiss() } }) show() listView?.post { listView?.requestAccessibilityFocus() } } } }) overflowMenuAdapter = adapter } private fun createDropDown(items: List<SelectionItem>, selected: SelectionItem) { Loading Loading @@ -542,6 +595,7 @@ class ControlsUiControllerImpl @Inject constructor ( } }) show() listView?.post { listView?.requestAccessibilityFocus() } } } }) Loading Loading @@ -631,7 +685,7 @@ class ControlsUiControllerImpl @Inject constructor ( .putString(PREF_COMPONENT, selectedItem.componentName.flattenToString()) .putString(PREF_STRUCTURE_OR_APP_NAME, selectedItem.name.toString()) .putBoolean(PREF_IS_PANEL, selectedItem is SelectedItem.PanelItem) .commit() .apply() } private fun maybeUpdateSelectedItem(item: SelectionItem): Boolean { Loading
packages/SystemUI/src/com/android/systemui/controls/ui/OverflowMenuAdapter.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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.systemui.controls.ui import android.content.Context import android.widget.ArrayAdapter import androidx.annotation.LayoutRes open class OverflowMenuAdapter( context: Context, @LayoutRes layoutId: Int, itemsWithIds: List<MenuItem>, private val isEnabledInternal: OverflowMenuAdapter.(Int) -> Boolean ) : ArrayAdapter<CharSequence>(context, layoutId, itemsWithIds.map(MenuItem::text)) { private val ids = itemsWithIds.map(MenuItem::id) override fun getItemId(position: Int): Long { return ids[position] } override fun isEnabled(position: Int): Boolean { return isEnabledInternal(position) } data class MenuItem(val text: CharSequence, val id: Long) }
packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ServiceInfo import android.os.UserHandle import android.service.controls.ControlsProviderService Loading Loading @@ -95,6 +96,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() { @Mock lateinit var dumpManager: DumpManager @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository @Mock lateinit var featureFlags: FeatureFlags @Mock lateinit var packageManager: PackageManager val sharedPreferences = FakeSharedPreferences() lateinit var controlsSettingsRepository: FakeControlsSettingsRepository Loading Loading @@ -124,6 +126,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() { ControlsUiControllerImpl( Lazy { controlsController }, context, packageManager, uiExecutor, bgExecutor, Lazy { controlsListingController }, Loading
packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * 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.systemui.controls.ui import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class OverflowMenuAdapterTest : SysuiTestCase() { @Test fun testGetItemId() { val ids = listOf(27L, 73L) val labels = listOf("first", "second") val adapter = OverflowMenuAdapter( context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } ) { true } ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) } } @Test fun testCheckEnabled() { val ids = listOf(27L, 73L) val labels = listOf("first", "second") val adapter = OverflowMenuAdapter( context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } ) { position -> position == 0 } assertThat(adapter.isEnabled(0)).isTrue() assertThat(adapter.isEnabled(1)).isFalse() } }