Merge "Add Open app in overflow menu" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2f927cffc8
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>,
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||
if (animateExtra) {
|
||||
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||
}
|
||||
|
||||
if (keyguardStateController.isShowing()) {
|
||||
activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
} else {
|
||||
R.string.controls_menu_add
|
||||
|
||||
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 {
|
||||
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 {
|
||||
@@ -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) {
|
||||
@@ -542,6 +595,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
}
|
||||
})
|
||||
show()
|
||||
listView?.post { listView?.requestAccessibilityFocus() }
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -124,6 +126,7 @@ class ControlsUiControllerImplTest : SysuiTestCase() {
|
||||
ControlsUiControllerImpl(
|
||||
Lazy { controlsController },
|
||||
context,
|
||||
packageManager,
|
||||
uiExecutor,
|
||||
bgExecutor,
|
||||
Lazy { controlsListingController },
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user