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

Commit 0f7c56a2 authored by Michał Brzeziński's avatar Michał Brzeziński Committed by Android (Google) Code Review
Browse files

Merge "Extracting buttons below expanded QS to a separate view and controller" into sc-v2-dev

parents fdf9c108 d3c5c568
Loading
Loading
Loading
Loading
+104 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 2021, 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.
-->
<com.android.systemui.qs.QSFooterActionsView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/qs_footer_actions_container"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:gravity="center_vertical">

    <com.android.systemui.statusbar.AlphaOptimizedImageView
        android:id="@android:id/edit"
        android:layout_width="0dp"
        android:layout_height="@dimen/qs_footer_action_button_size"
        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
        android:layout_weight="1"
        android:background="@drawable/qs_footer_action_chip_background"
        android:clickable="true"
        android:clipToPadding="false"
        android:contentDescription="@string/accessibility_quick_settings_edit"
        android:focusable="true"
        android:padding="@dimen/qs_footer_icon_padding"
        android:src="@*android:drawable/ic_mode_edit"
        android:tint="?android:attr/textColorPrimary" />

    <com.android.systemui.statusbar.phone.MultiUserSwitch
        android:id="@+id/multi_user_switch"
        android:layout_width="0dp"
        android:layout_height="@dimen/qs_footer_action_button_size"
        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
        android:layout_weight="1"
        android:background="@drawable/qs_footer_action_chip_background"
        android:focusable="true">

        <ImageView
            android:id="@+id/multi_user_avatar"
            android:layout_width="@dimen/multi_user_avatar_expanded_size"
            android:layout_height="@dimen/multi_user_avatar_expanded_size"
            android:layout_gravity="center"
            android:scaleType="centerInside" />
    </com.android.systemui.statusbar.phone.MultiUserSwitch>

    <com.android.systemui.statusbar.AlphaOptimizedImageView
        android:id="@+id/pm_lite"
        android:layout_width="0dp"
        android:layout_height="@dimen/qs_footer_action_button_size"
        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
        android:layout_weight="1"
        android:background="@drawable/qs_footer_action_chip_background"
        android:clickable="true"
        android:clipToPadding="false"
        android:focusable="true"
        android:padding="@dimen/qs_footer_icon_padding"
        android:src="@*android:drawable/ic_lock_power_off"
        android:contentDescription="@string/accessibility_quick_settings_power_menu"
        android:tint="?android:attr/textColorPrimary" />

    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
        android:id="@+id/settings_button_container"
        android:layout_width="0dp"
        android:layout_height="@dimen/qs_footer_action_button_size"
        android:background="@drawable/qs_footer_action_chip_background"
        android:layout_weight="1"
        android:clipChildren="false"
        android:clipToPadding="false">

        <com.android.systemui.statusbar.phone.SettingsButton
            android:id="@+id/settings_button"
            android:layout_width="match_parent"
            android:layout_height="@dimen/qs_footer_action_button_size"
            android:layout_gravity="center"
            android:contentDescription="@string/accessibility_quick_settings_settings"
            android:background="@drawable/qs_footer_action_chip_background_borderless"
            android:padding="@dimen/qs_footer_icon_padding"
            android:scaleType="centerInside"
            android:src="@drawable/ic_settings"
            android:tint="?android:attr/textColorPrimary" />

        <com.android.systemui.statusbar.AlphaOptimizedImageView
            android:id="@+id/tuner_icon"
            android:layout_width="8dp"
            android:layout_height="8dp"
            android:layout_gravity="center_horizontal|bottom"
            android:layout_marginBottom="@dimen/qs_footer_icon_padding"
            android:src="@drawable/tuner"
            android:tint="?android:attr/textColorTertiary"
            android:visibility="invisible" />

    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>

</com.android.systemui.qs.QSFooterActionsView>
 No newline at end of file
+1 −86
Original line number Diff line number Diff line
@@ -68,93 +68,8 @@

        </LinearLayout>

        <LinearLayout
            android:id="@+id/qs_footer_actions_container"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:gravity="center_vertical">

            <com.android.systemui.statusbar.AlphaOptimizedImageView
                android:id="@android:id/edit"
                android:layout_width="0dp"
                android:layout_height="@dimen/qs_footer_action_button_size"
                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
                android:layout_weight="1"
                android:background="@drawable/qs_footer_action_chip_background"
                android:clickable="true"
                android:clipToPadding="false"
                android:contentDescription="@string/accessibility_quick_settings_edit"
                android:focusable="true"
                android:padding="@dimen/qs_footer_icon_padding"
                android:src="@*android:drawable/ic_mode_edit"
                android:tint="?android:attr/textColorPrimary" />

            <com.android.systemui.statusbar.phone.MultiUserSwitch
                android:id="@+id/multi_user_switch"
                android:layout_width="0dp"
                android:layout_height="@dimen/qs_footer_action_button_size"
                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
                android:layout_weight="1"
                android:background="@drawable/qs_footer_action_chip_background"
                android:focusable="true">
        <include layout="@layout/qs_footer_actions"/>

                <ImageView
                    android:id="@+id/multi_user_avatar"
                    android:layout_width="@dimen/multi_user_avatar_expanded_size"
                    android:layout_height="@dimen/multi_user_avatar_expanded_size"
                    android:layout_gravity="center"
                    android:scaleType="centerInside" />
            </com.android.systemui.statusbar.phone.MultiUserSwitch>

            <com.android.systemui.statusbar.AlphaOptimizedImageView
                android:id="@+id/pm_lite"
                android:layout_width="0dp"
                android:layout_height="@dimen/qs_footer_action_button_size"
                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
                android:layout_weight="1"
                android:background="@drawable/qs_footer_action_chip_background"
                android:clickable="true"
                android:clipToPadding="false"
                android:focusable="true"
                android:padding="@dimen/qs_footer_icon_padding"
                android:src="@*android:drawable/ic_lock_power_off"
                android:contentDescription="@string/accessibility_quick_settings_power_menu"
                android:tint="?android:attr/textColorPrimary" />

            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                android:id="@+id/settings_button_container"
                android:layout_width="0dp"
                android:layout_height="@dimen/qs_footer_action_button_size"
                android:background="@drawable/qs_footer_action_chip_background"
                android:layout_weight="1"
                android:clipChildren="false"
                android:clipToPadding="false">

                <com.android.systemui.statusbar.phone.SettingsButton
                    android:id="@+id/settings_button"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/qs_footer_action_button_size"
                    android:layout_gravity="center"
                    android:contentDescription="@string/accessibility_quick_settings_settings"
                    android:background="@drawable/qs_footer_action_chip_background_borderless"
                    android:padding="@dimen/qs_footer_icon_padding"
                    android:scaleType="centerInside"
                    android:src="@drawable/ic_settings"
                    android:tint="?android:attr/textColorPrimary" />

                <com.android.systemui.statusbar.AlphaOptimizedImageView
                    android:id="@+id/tuner_icon"
                    android:layout_width="8dp"
                    android:layout_height="8dp"
                    android:layout_gravity="center_horizontal|bottom"
                    android:layout_marginBottom="@dimen/qs_footer_icon_padding"
                    android:src="@drawable/tuner"
                    android:tint="?android:attr/textColorTertiary"
                    android:visibility="invisible" />

            </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>

        </LinearLayout>
    </LinearLayout>

</com.android.systemui.qs.QSFooterView>
+0 −5
Original line number Diff line number Diff line
@@ -34,11 +34,6 @@ public interface QSFooter {
     */
    void setExpanded(boolean expanded);

    /**
     * Returns the full height of the footer.
     */
    int getHeight();

    /**
     * Sets the percentage amount that the quick settings has been expanded.
     *
+167 −0
Original line number Diff line number Diff line
package com.android.systemui.qs

import android.content.Intent
import android.os.UserManager
import android.provider.Settings
import android.view.View
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.phone.SettingsButton
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.ViewController
import javax.inject.Inject
import javax.inject.Named

class QSFooterActionsController @Inject constructor(
    view: QSFooterActionsView,
    private val qsPanelController: QSPanelController,
    private val activityStarter: ActivityStarter,
    private val userManager: UserManager,
    private val userInfoController: UserInfoController,
    private val multiUserSwitchController: MultiUserSwitchController,
    private val deviceProvisionedController: DeviceProvisionedController,
    private val falsingManager: FalsingManager,
    private val metricsLogger: MetricsLogger,
    private val tunerService: TunerService,
    private val globalActionsDialog: GlobalActionsDialogLite,
    private val uiEventLogger: UiEventLogger,
    @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean
) : ViewController<QSFooterActionsView>(view) {

    private var listening: Boolean = false
    var expanded = false
        set(value) {
            field = value
            mView.setExpanded(value, isTunerEnabled(),
                    multiUserSwitchController.isMultiUserEnabled)
        }

    private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
    private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
    private val editButton: View = view.findViewById(android.R.id.edit)
    private val powerMenuLite: View = view.findViewById(R.id.pm_lite)

    private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
        val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
        mView.onUserInfoChanged(picture, isGuestUser)
    }

    private val onClickListener = View.OnClickListener { v ->
        // Don't do anything until views are unhidden. Don't do anything if the tap looks
        // suspicious.
        if (!expanded || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
            return@OnClickListener
        }
        if (v === settingsButton) {
            if (!deviceProvisionedController.isCurrentUserSetup) {
                // If user isn't setup just unlock the device and dump them back at SUW.
                activityStarter.postQSRunnableDismissingKeyguard {}
                return@OnClickListener
            }
            metricsLogger.action(
                    if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
                    else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
            if (settingsButton.isTunerClick) {
                activityStarter.postQSRunnableDismissingKeyguard {
                    if (isTunerEnabled()) {
                        tunerService.showResetRequest {
                            // Relaunch settings so that the tuner disappears.
                            startSettingsActivity()
                        }
                    } else {
                        Toast.makeText(context, R.string.tuner_toast, Toast.LENGTH_LONG).show()
                        tunerService.isTunerEnabled = true
                    }
                    startSettingsActivity()
                }
            } else {
                startSettingsActivity()
            }
        } else if (v === powerMenuLite) {
            uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
            globalActionsDialog.showOrHideDialog(false, true)
        }
    }

    override fun onInit() {
        multiUserSwitchController.init()
    }

    private fun startSettingsActivity() {
        val animationController = settingsButtonContainer?.let {
            ActivityLaunchAnimator.Controller.fromView(
                    it,
                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON)
            }
        activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS),
                true /* dismissShade */, animationController)
    }

    @VisibleForTesting
    public override fun onViewAttached() {
        if (showPMLiteButton) {
            powerMenuLite.visibility = View.VISIBLE
            powerMenuLite.setOnClickListener(onClickListener)
        } else {
            powerMenuLite.visibility = View.GONE
        }
        settingsButton.setOnClickListener(onClickListener)
        editButton.setOnClickListener(View.OnClickListener { view: View? ->
            if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                return@OnClickListener
            }
            activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
        })

        mView.updateEverything(isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
    }

    override fun onViewDetached() {
        setListening(false)
    }

    fun setListening(listening: Boolean) {
        if (this.listening == listening) {
            return
        }
        this.listening = listening
        if (this.listening) {
            userInfoController.addCallback(onUserInfoChangedListener)
        } else {
            userInfoController.removeCallback(onUserInfoChangedListener)
        }
    }

    fun disable(state2: Int) {
        mView.disable(state2, isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
    }

    fun setExpansion(headerExpansionFraction: Float) {
        mView.setExpansion(headerExpansionFraction)
    }

    fun updateAnimator(width: Int, numTiles: Int) {
        mView.updateAnimator(width, numTiles)
    }

    fun setKeyguardShowing() {
        mView.setKeyguardShowing()
    }

    private fun isTunerEnabled() = tunerService.isTunerEnabled
}
 No newline at end of file
+159 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.qs

import android.app.StatusBarManager
import android.content.Context
import android.content.res.Configuration
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
import android.os.UserManager
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
import com.android.systemui.R
import com.android.systemui.statusbar.phone.MultiUserSwitch
import com.android.systemui.statusbar.phone.SettingsButton

/**
 * Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS,
 * in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
 * edit tiles, power off and conditionally: user switch and tuner
 */
class QSFooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
    private lateinit var settingsContainer: View
    private lateinit var settingsButton: SettingsButton
    private lateinit var multiUserSwitch: MultiUserSwitch
    private lateinit var multiUserAvatar: ImageView
    private lateinit var tunerIcon: View
    private lateinit var editTilesButton: View

    private var settingsCogAnimator: TouchAnimator? = null

    private var qsDisabled = false
    private var isExpanded = false
    private var expansionAmount = 0f

    override fun onFinishInflate() {
        super.onFinishInflate()
        editTilesButton = requireViewById(android.R.id.edit)
        settingsButton = findViewById(R.id.settings_button)
        settingsContainer = findViewById(R.id.settings_button_container)
        multiUserSwitch = findViewById(R.id.multi_user_switch)
        multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
        tunerIcon = requireViewById(R.id.tuner_icon)

        // RenderThread is doing more harm than good when touching the header (to expand quick
        // settings), so disable it for this view
        if (settingsButton.background is RippleDrawable) {
            (settingsButton.background as RippleDrawable).setForceSoftware(true)
        }
        updateResources()
        importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
    }

    fun updateAnimator(width: Int, numTiles: Int) {
        val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
                mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
        val remaining = (width - numTiles * size) / (numTiles - 1)
        val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
        val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
        settingsCogAnimator = TouchAnimator.Builder()
                .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
                .addFloat(settingsButton, "rotation", -120f, 0f)
                .build()
        setExpansion(expansionAmount)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        updateResources()
    }

    override fun onRtlPropertiesChanged(layoutDirection: Int) {
        super.onRtlPropertiesChanged(layoutDirection)
        updateResources()
    }

    private fun updateResources() {
        val tunerIconTranslation = mContext.resources
                .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation).toFloat()
        tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
    }

    fun setKeyguardShowing() {
        setExpansion(expansionAmount)
    }

    fun setExpanded(expanded: Boolean, isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
        if (isExpanded == expanded) return
        isExpanded = expanded
        updateEverything(isTunerEnabled, multiUserEnabled)
    }

    fun setExpansion(headerExpansionFraction: Float) {
        expansionAmount = headerExpansionFraction
        if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
    }

    fun disable(state2: Int, isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
        val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
        if (disabled == qsDisabled) return
        qsDisabled = disabled
        updateEverything(isTunerEnabled, multiUserEnabled)
    }

    fun updateEverything(isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
        post {
            updateVisibilities(isTunerEnabled, multiUserEnabled)
            updateClickabilities()
            isClickable = false
        }
    }

    private fun updateClickabilities() {
        multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
        editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
        settingsButton.isClickable = settingsButton.visibility == VISIBLE
    }

    private fun updateVisibilities(isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
        settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
        tunerIcon.visibility = if (isTunerEnabled) VISIBLE else INVISIBLE
        multiUserSwitch.visibility = if (showUserSwitcher(multiUserEnabled)) VISIBLE else GONE
        val isDemo = UserManager.isDeviceInDemoMode(context)
        settingsButton.visibility = if (isDemo || !isExpanded) INVISIBLE else VISIBLE
    }

    private fun showUserSwitcher(multiUserEnabled: Boolean): Boolean {
        return isExpanded && multiUserEnabled
    }

    fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) {
        var pictureToSet = picture
        if (picture != null && isGuestUser && picture !is UserIconDrawable) {
            pictureToSet = picture.constantState.newDrawable(resources).mutate()
            pictureToSet.setColorFilter(
                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
                    PorterDuff.Mode.SRC_IN)
        }
        multiUserAvatar.setImageDrawable(pictureToSet)
    }
}
 No newline at end of file
Loading