Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f927cff authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Add Open app in overflow menu" into tm-qpr-dev

parents 78f1af0b 14682048
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+84 −30
Original line number Diff line number Diff line
@@ -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
        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

        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 {
@@ -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 {
+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)
}
+3 −0
Original line number Diff line number Diff line
@@ -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 },
+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()
    }
}