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

Commit d94bc0fe authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Automerger Merge Worker
Browse files

Merge changes Ic5ef8de2,I9b43b526,Idce510fc into tm-qpr-dev am: 8de3d142

parents 6b424e88 8de3d142
Loading
Loading
Loading
Loading
+19 −10
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.receiver
import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
@@ -44,6 +45,7 @@ import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject

/**
@@ -63,6 +65,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
        powerManager: PowerManager,
        @Main private val mainHandler: Handler,
        private val uiEventLogger: MediaTttReceiverUiEventLogger,
        private val viewUtil: ViewUtil,
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
        context,
        logger,
@@ -83,7 +86,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
        height = WindowManager.LayoutParams.MATCH_PARENT
        layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
        fitInsetsTypes = 0 // Ignore insets from all system bars
        flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    }

    private val commandQueueCallbacks = object : CommandQueue.Callbacks {
@@ -140,8 +142,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
    }

    override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
        super.updateView(newInfo, currentView)

        val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
            context, newInfo.routeInfo.clientPackageName, logger
        )
@@ -154,14 +154,14 @@ class MediaTttChipControllerReceiver @Inject constructor(
                context.resources.getDimensionPixelSize(R.dimen.media_ttt_generic_icon_padding)
            }

        val iconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
        val iconView = currentView.getAppIconView()
        iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
        iconView.setImageDrawable(iconDrawable)
        iconView.contentDescription = iconContentDescription
    }

    override fun animateViewIn(view: ViewGroup) {
        val appIconView = view.requireViewById<View>(R.id.app_icon)
        val appIconView = view.getAppIconView()
        appIconView.animate()
                .translationYBy(-1 * getTranslationAmount().toFloat())
                .setDuration(30.frames)
@@ -175,6 +175,12 @@ class MediaTttChipControllerReceiver @Inject constructor(
        startRipple(view.requireViewById(R.id.ripple))
    }

    override fun getTouchableRegion(view: View, outRect: Rect) {
        // Even though the app icon view isn't touchable, users might think it is. So, use it as the
        // touchable region to ensure that touches don't get passed to the window below.
        viewUtil.setRectToViewWindowLocation(view.getAppIconView(), outRect)
    }

    /** Returns the amount that the chip will be translated by in its intro animation. */
    private fun getTranslationAmount(): Int {
        return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
@@ -202,16 +208,19 @@ class MediaTttChipControllerReceiver @Inject constructor(

    private fun layoutRipple(rippleView: ReceiverChipRippleView) {
        val windowBounds = windowManager.currentWindowMetrics.bounds
        val height = windowBounds.height()
        val width = windowBounds.width()
        val height = windowBounds.height().toFloat()
        val width = windowBounds.width().toFloat()

        val maxDiameter = height / 2.5f
        rippleView.setMaxSize(maxDiameter, maxDiameter)
        rippleView.setMaxSize(width / 2f, height / 2f)
        // Center the ripple on the bottom of the screen in the middle.
        rippleView.setCenter(width * 0.5f, height.toFloat())
        rippleView.setCenter(width * 0.5f, height)
        val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
        rippleView.setColor(color, 70)
    }

    private fun View.getAppIconView(): CachingIconView {
        return this.requireViewById(R.id.app_icon)
    }
}

data class ChipReceiverInfo(
+3 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver

import android.content.Context
import android.util.AttributeSet
import com.android.systemui.ripple.RippleShader
import com.android.systemui.ripple.RippleView

/**
@@ -25,9 +26,9 @@ import com.android.systemui.ripple.RippleView
 */
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
    init {
        // TODO: use RippleShape#ELLIPSE when calling setupShader.
        setupShader()
        setupShader(RippleShader.RippleShape.ELLIPSE)
        setRippleFill(true)
        setSparkleStrength(0f)
        duration = 3000L
    }
}
+9 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.sender

import android.app.StatusBarManager
import android.content.Context
import android.graphics.Rect
import android.media.MediaRoute2Info
import android.os.PowerManager
import android.util.Log
@@ -46,6 +47,7 @@ import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
import dagger.Lazy
import javax.inject.Inject

@@ -68,6 +70,7 @@ open class MediaTttChipControllerSender @Inject constructor(
        // And overcome performance issue, check [b/247817628] for details.
        private val falsingManager: Lazy<FalsingManager>,
        private val falsingCollector: Lazy<FalsingCollector>,
        private val viewUtil: ViewUtil,
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
        context,
        logger,
@@ -129,8 +132,6 @@ open class MediaTttChipControllerSender @Inject constructor(
        newInfo: ChipSenderInfo,
        currentView: ViewGroup
    ) {
        super.updateView(newInfo, currentView)

        val chipState = newInfo.state

        // Detect falsing touches on the chip.
@@ -207,10 +208,10 @@ open class MediaTttChipControllerSender @Inject constructor(
        //   animateChipOut matches the animateChipIn.
    }

    override fun shouldIgnoreViewRemoval(removalReason: String): Boolean {
    override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean {
        // Don't remove the chip if we're in progress or succeeded, since the user should still be
        // able to see the status of the transfer. (But do remove it if it's finally timed out.)
        val transferStatus = info?.state?.transferStatus
        val transferStatus = info.state.transferStatus
        if (
            (transferStatus == TransferStatus.IN_PROGRESS ||
                transferStatus == TransferStatus.SUCCEEDED) &&
@@ -224,6 +225,10 @@ open class MediaTttChipControllerSender @Inject constructor(
        return false
    }

    override fun getTouchableRegion(view: View, outRect: Rect) {
        viewUtil.setRectToViewWindowLocation(view, outRect)
    }

    private fun Boolean.visibleIfTrue(): Int {
        return if (this) {
            View.VISIBLE
+43 −26
Original line number Diff line number Diff line
@@ -20,17 +20,18 @@ import android.annotation.LayoutRes
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.PowerManager
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -70,7 +71,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
        width = WindowManager.LayoutParams.WRAP_CONTENT
        height = WindowManager.LayoutParams.WRAP_CONTENT
        type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
        flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        title = windowTitle
        format = PixelFormat.TRANSLUCENT
        setTrustedOverlay()
@@ -84,11 +86,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
     */
    internal abstract val windowLayoutParams: WindowManager.LayoutParams

    /** The view currently being displayed. Null if the view is not being displayed. */
    private var view: ViewGroup? = null

    /** The info currently being displayed. Null if the view is not being displayed. */
    internal var info: T? = null
    /** A container for all the display-related objects. Null if the view is not being displayed. */
    private var displayInfo: DisplayInfo? = null

    /** A [Runnable] that, when run, will cancel the pending timeout of the view. */
    private var cancelViewTimeout: Runnable? = null
@@ -100,10 +99,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
     * display the correct information in the view.
     */
    fun displayView(newInfo: T) {
        val currentView = view
        val currentDisplayInfo = displayInfo

        if (currentView != null) {
            updateView(newInfo, currentView)
        if (currentDisplayInfo != null) {
            currentDisplayInfo.info = newInfo
            updateView(currentDisplayInfo.info, currentDisplayInfo.view)
        } else {
            // The view is new, so set up all our callbacks and inflate the view
            configurationController.addCallback(displayScaleListener)
@@ -140,19 +140,24 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
        val newView = LayoutInflater
                .from(context)
                .inflate(viewLayoutRes, null) as ViewGroup
        view = newView
        updateView(newInfo, newView)
        val newViewController = TouchableRegionViewController(newView, this::getTouchableRegion)
        newViewController.init()

        // We don't need to hold on to the view controller since we never set anything additional
        // on it -- it will be automatically cleaned up when the view is detached.
        val newDisplayInfo = DisplayInfo(newView, newInfo)
        displayInfo = newDisplayInfo
        updateView(newDisplayInfo.info, newDisplayInfo.view)
        windowManager.addView(newView, windowLayoutParams)
        animateViewIn(newView)
    }

    /** Removes then re-inflates the view. */
    private fun reinflateView() {
        val currentInfo = info
        if (view == null || currentInfo == null) { return }
        val currentViewInfo = displayInfo ?: return

        windowManager.removeView(view)
        inflateAndUpdateView(currentInfo)
        windowManager.removeView(currentViewInfo.view)
        inflateAndUpdateView(currentViewInfo.info)
    }

    private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
@@ -168,20 +173,20 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
     *     change, etc.)
     */
    fun removeView(removalReason: String) {
        if (shouldIgnoreViewRemoval(removalReason)) {
        val currentDisplayInfo = displayInfo ?: return
        if (shouldIgnoreViewRemoval(currentDisplayInfo.info, removalReason)) {
            return
        }
        val currentView = view ?: return

        val currentView = currentDisplayInfo.view
        animateViewOut(currentView) { windowManager.removeView(currentView) }

        logger.logChipRemoval(removalReason)
        configurationController.removeCallback(displayScaleListener)
        // Re-set the view to null immediately (instead as part of the animation end runnable) so
        // Re-set to null immediately (instead as part of the animation end runnable) so
        // that if a new view event comes in while this view is animating out, we still display the
        // new view appropriately.
        view = null
        info = null
        displayInfo = null
        // No need to time the view out since it's already gone
        cancelViewTimeout?.run()
    }
@@ -191,15 +196,18 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
     *
     * Allows subclasses to keep the view visible for longer in certain circumstances.
     */
    open fun shouldIgnoreViewRemoval(removalReason: String): Boolean = false
    open fun shouldIgnoreViewRemoval(info: T, removalReason: String): Boolean = false

    /**
     * A method implemented by subclasses to update [currentView] based on [newInfo].
     */
    @CallSuper
    open fun updateView(newInfo: T, currentView: ViewGroup) {
        info = newInfo
    }
    abstract fun updateView(newInfo: T, currentView: ViewGroup)

    /**
     * Fills [outRect] with the touchable region of this view. This will be used by WindowManager
     * to decide which touch events go to the view.
     */
    abstract fun getTouchableRegion(view: View, outRect: Rect)

    /**
     * A method that can be implemented by subclasses to do custom animations for when the view
@@ -216,6 +224,15 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
    internal open fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
        onAnimationEnd.run()
    }

    /** A container for all the display-related state objects. */
    private inner class DisplayInfo(
        /** The view currently being displayed. */
        val view: ViewGroup,

        /** The info currently being displayed. */
        var info: T,
    )
}

object TemporaryDisplayRemovalReason {
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.temporarydisplay

import android.graphics.Rect
import android.view.View
import android.view.ViewTreeObserver
import com.android.systemui.util.ViewController

/**
 * A view controller that will notify the [ViewTreeObserver] about the touchable region for this
 * view. This will be used by WindowManager to decide which touch events go to the view and which
 * pass through to the window below.
 *
 * @param touchableRegionSetter a function that, given the view and an out rect, fills the rect with
 * the touchable region of this view.
 */
class TouchableRegionViewController(
    view: View,
    touchableRegionSetter: (View, Rect) -> Unit,
) : ViewController<View>(view) {

    private val tempRect = Rect()

    private val internalInsetsListener =
        ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo ->
            inoutInfo.setTouchableInsets(
                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
            )

            tempRect.setEmpty()
            touchableRegionSetter.invoke(mView, tempRect)
            inoutInfo.touchableRegion.set(tempRect)
        }

    public override fun onViewAttached() {
        mView.viewTreeObserver.addOnComputeInternalInsetsListener(internalInsetsListener)
    }

    public override fun onViewDetached() {
        mView.viewTreeObserver.removeOnComputeInternalInsetsListener(internalInsetsListener)
    }
}
Loading