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

Commit 4927ffbe authored by Ivan Tkachenko's avatar Ivan Tkachenko Committed by Android (Google) Code Review
Browse files

Merge "Share bubbles dismiss functionality with Launcher" into udc-qpr-dev

parents 8779fae8 1ef7e182
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -42,16 +42,19 @@ filegroup {
filegroup {
    name: "wm_shell_util-sources",
    srcs: [
        "src/com/android/wm/shell/util/**/*.java",
        "src/com/android/wm/shell/animation/Interpolators.java",
        "src/com/android/wm/shell/animation/PhysicsAnimator.kt",
        "src/com/android/wm/shell/common/bubbles/*.kt",
        "src/com/android/wm/shell/common/bubbles/*.java",
        "src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt",
        "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
        "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
        "src/com/android/wm/shell/common/TransactionPool.java",
        "src/com/android/wm/shell/common/bubbles/*.java",
        "src/com/android/wm/shell/common/TriangleShape.java",
        "src/com/android/wm/shell/animation/Interpolators.java",
        "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
        "src/com/android/wm/shell/pip/PipContentOverlay.java",
        "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
        "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
        "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
        "src/com/android/wm/shell/util/**/*.java",
    ],
    path: "src",
}
+3 −0
Original line number Diff line number Diff line
@@ -88,6 +88,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.bubbles.RelativeTouchListener;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;

import java.io.PrintWriter;
@@ -1179,6 +1181,7 @@ public class BubbleStackView extends FrameLayout
            removeView(mDismissView);
        }
        mDismissView = new DismissView(getContext());
        DismissViewUtils.setup(mDismissView);
        int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation);

        addView(mDismissView);
+33 −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.
 */
@file:JvmName("DismissViewUtils")

package com.android.wm.shell.bubbles

import com.android.wm.shell.R
import com.android.wm.shell.common.bubbles.DismissView

fun DismissView.setup() {
    setup(DismissView.Config(
            targetSizeResId = R.dimen.dismiss_circle_size,
            iconSizeResId = R.dimen.dismiss_target_x_size,
            bottomMarginResId = R.dimen.floating_dismiss_bottom_margin,
            floatingGradientHeightResId = R.dimen.floating_dismiss_gradient_height,
            floatingGradientColorResId = android.R.color.system_neutral1_900,
            backgroundResId = R.drawable.dismiss_circle_background,
            iconResId = R.drawable.pip_ic_close_white
    ))
}
 No newline at end of file
+26 −14
Original line number Diff line number Diff line
@@ -14,16 +14,17 @@
 * limitations under the License.
 */

package com.android.wm.shell.common;
package com.android.wm.shell.common.bubbles;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.android.wm.shell.R;
import androidx.annotation.DimenRes;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;

/**
 * Circular view with a semitransparent, circular background with an 'X' inside it.
@@ -31,33 +32,44 @@ import com.android.wm.shell.R;
 * This is used by both Bubbles and PIP as the dismiss target.
 */
public class DismissCircleView extends FrameLayout {
    @DrawableRes int mBackgroundResId;
    @DimenRes int mIconSizeResId;

    private final ImageView mIconView = new ImageView(getContext());

    public DismissCircleView(Context context) {
        super(context);
        final Resources res = getResources();

        setBackground(res.getDrawable(R.drawable.dismiss_circle_background));

        mIconView.setImageDrawable(res.getDrawable(R.drawable.pip_ic_close_white));
        addView(mIconView);

        setViewSizes();
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        final Resources res = getResources();
        setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
        setBackground(ContextCompat.getDrawable(getContext(), mBackgroundResId));
        setViewSizes();
    }

    /**
     * Sets up view with the provided resource ids.
     * Decouples resource dependency in order to be used externally (e.g. Launcher)
     *
     * @param backgroundResId drawable resource id of the circle background
     * @param iconResId drawable resource id of the icon for the dismiss view
     * @param iconSizeResId dimen resource id of the icon size
     */
    public void setup(@DrawableRes int backgroundResId, @DrawableRes int iconResId,
            @DimenRes int iconSizeResId) {
        mBackgroundResId = backgroundResId;
        mIconSizeResId = iconSizeResId;

        setBackground(ContextCompat.getDrawable(getContext(), backgroundResId));
        mIconView.setImageDrawable(ContextCompat.getDrawable(getContext(), iconResId));
        setViewSizes();
    }

    /** Retrieves the current dimensions for the icon and circle and applies them. */
    private void setViewSizes() {
        final Resources res = getResources();
        final int iconSize = res.getDimensionPixelSize(R.dimen.dismiss_target_x_size);
        final int iconSize = getResources().getDimensionPixelSize(mIconSizeResId);
        mIconView.setLayoutParams(
                new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
    }
+88 −25
Original line number Diff line number Diff line
@@ -14,41 +14,73 @@
 * limitations under the License.
 */

package com.android.wm.shell.bubbles
package com.android.wm.shell.common.bubbles

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.util.IntProperty
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
import com.android.wm.shell.common.DismissCircleView

/*
/**
 * View that handles interactions between DismissCircleView and BubbleStackView.
 *
 * @note [setup] method should be called after initialisation
 */
class DismissView(context: Context) : FrameLayout(context) {
    /**
     * The configuration is used to provide module specific resource ids
     *
     * @see [setup] method
     */
    data class Config(
            /** dimen resource id of the dismiss target circle view size */
            @DimenRes val targetSizeResId: Int,
            /** dimen resource id of the icon size in the dismiss target */
            @DimenRes val iconSizeResId: Int,
            /** dimen resource id of the bottom margin for the dismiss target */
            @DimenRes var bottomMarginResId: Int,
            /** dimen resource id of the height for dismiss area gradient */
            @DimenRes val floatingGradientHeightResId: Int,
            /** color resource id of the dismiss area gradient color */
            @ColorRes val floatingGradientColorResId: Int,
            /** drawable resource id of the dismiss target background */
            @DrawableRes val backgroundResId: Int,
            /** drawable resource id of the icon for the dismiss target */
            @DrawableRes val iconResId: Int
    )

    companion object {
        private const val SHOULD_SETUP =
                "The view isn't ready. Should be called after `setup`"
        private val TAG = DismissView::class.simpleName
    }

    var circle = DismissCircleView(context)
    var isShowing = false
    var targetSizeResId: Int
    var config: Config? = null

    private val animator = PhysicsAnimator.getInstance(circle)
    private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
    private val DISMISS_SCRIM_FADE_MS = 200L
    private var wm: WindowManager =
            context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    private var gradientDrawable = createGradient()
    private var gradientDrawable: GradientDrawable? = null

    private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
            object : IntProperty<GradientDrawable>("alpha") {
@@ -61,23 +93,41 @@ class DismissView(context: Context) : FrameLayout(context) {
    }

    init {
        setLayoutParams(LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
            Gravity.BOTTOM))
        updatePadding()
        setClipToPadding(false)
        setClipChildren(false)
        setVisibility(View.INVISIBLE)
        addView(circle)
    }

    /**
     * Sets up view with the provided resource ids.
     *
     * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called
     * with default params in module specific extension:
     * @see [DismissView.setup] in DismissViewExt.kt
     */
    fun setup(config: Config) {
        this.config = config

        // Setup layout
        layoutParams = LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                resources.getDimensionPixelSize(config.floatingGradientHeightResId),
                Gravity.BOTTOM)
        updatePadding()

        // Setup gradient
        gradientDrawable = createGradient(color = config.floatingGradientColorResId)
        setBackgroundDrawable(gradientDrawable)

        targetSizeResId = R.dimen.dismiss_circle_size
        val targetSize: Int = resources.getDimensionPixelSize(targetSizeResId)
        addView(circle, LayoutParams(targetSize, targetSize,
                Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
        // start with circle offscreen so it's animated up
        circle.setTranslationY(resources.getDimensionPixelSize(
                R.dimen.floating_dismiss_gradient_height).toFloat())
        // Setup DismissCircleView
        circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId)
        val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId)
        circle.layoutParams = LayoutParams(targetSize, targetSize,
                Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
        // Initial position with circle offscreen so it's animated up
        circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
                .toFloat()
    }

    /**
@@ -85,6 +135,7 @@ class DismissView(context: Context) : FrameLayout(context) {
     */
    fun show() {
        if (isShowing) return
        val gradientDrawable = checkExists(gradientDrawable) ?: return
        isShowing = true
        setVisibility(View.VISIBLE)
        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
@@ -104,6 +155,7 @@ class DismissView(context: Context) : FrameLayout(context) {
     */
    fun hide() {
        if (!isShowing) return
        val gradientDrawable = checkExists(gradientDrawable) ?: return
        isShowing = false
        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
                gradientDrawable.alpha, 0)
@@ -124,18 +176,17 @@ class DismissView(context: Context) : FrameLayout(context) {
    }

    fun updateResources() {
        val config = checkExists(config) ?: return
        updatePadding()
        layoutParams.height = resources.getDimensionPixelSize(
                R.dimen.floating_dismiss_gradient_height)

        val targetSize = resources.getDimensionPixelSize(targetSizeResId)
        layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
        val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
        circle.layoutParams.width = targetSize
        circle.layoutParams.height = targetSize
        circle.requestLayout()
    }

    private fun createGradient(): GradientDrawable {
        val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900)
    private fun createGradient(@ColorRes color: Int): GradientDrawable {
        val gradientColor = ContextCompat.getColor(context, color)
        val alpha = 0.7f * 255
        val gradientColorWithAlpha = Color.argb(alpha.toInt(),
                Color.red(gradientColor),
@@ -150,10 +201,22 @@ class DismissView(context: Context) : FrameLayout(context) {
    }

    private fun updatePadding() {
        val config = checkExists(config) ?: return
        val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
        val navInset = insets.getInsetsIgnoringVisibility(
                WindowInsets.Type.navigationBars())
        setPadding(0, 0, 0, navInset.bottom +
                resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
                resources.getDimensionPixelSize(config.bottomMarginResId))
    }

    /**
     * Checks if the value is set up and exists, if not logs an exception.
     * Used for convenient logging in case `setup` wasn't called before
     *
     * @return value provided as argument
     */
    private fun <T>checkExists(value: T?): T? {
        if (value == null) Log.e(TAG, SHOULD_SETUP)
        return value
    }
}
Loading