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

Commit 6ce85f69 authored by Matt Casey's avatar Matt Casey
Browse files

Entrance and exit animations for work profile first run.

Slide up from the bottom, fade in. Animation happens after the
screenshot entrance animation (same time the view appeared before). This
isn't meant to be a final animation, just to put something in so it's
not so jarring.

Includes some changes in preparation for the message area being able to
support multiple types of content:
1. Moved all interactions with the view to MessageContainerController,
   which will be able to handle different types of content. This isn't
   entirely generic as it does contain the view code for the work
   profile first run.
2. Moved work profile first run view code to its own XML to let
   different content swap in more easily.

The animations themselves are only called by flag-protected code, but a
couple of aspects are not fully flag-protected:
1. The message container view is now INVISIBLE instead of GONE, and is
   shifted down (y-axis) to be outside of the containing ConstraintLayout.
2. The message container is attached to a Guideline which starts off at
   the bottom of the screen. The actions container sits atop that
   guideline. No user-visible change, just expressed with a slightly
   different structure.
3. Slight increase in the bottom margin of the actions container and the
   message container, as it was being clipped by the navbar.

Test: Visual inspection of the flow
Bug: 254245929
Change-Id: Ie1b97f841bb28de52a8a9ea9ac5ed48c3f694abc
parent 89c8a284
Loading
Loading
Loading
Loading
+13 −42
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/actions_container"
        app:layout_constraintEnd_toEndOf="@+id/actions_container"
        app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"/>
        app:layout_constraintBottom_toTopOf="@id/guideline"/>
    <HorizontalScrollView
        android:id="@+id/actions_container"
        android:layout_width="0dp"
@@ -127,57 +127,28 @@
        app:layout_constraintTop_toTopOf="@id/screenshot_preview"
        android:elevation="7dp"/>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_end="0dp" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/screenshot_message_container"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
        android:layout_marginVertical="4dp"
        android:layout_marginTop="4dp"
        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
        android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
        android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
        android:elevation="4dp"
        android:background="@drawable/action_chip_container_background"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">

        <ImageView
            android:id="@+id/screenshot_message_icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:paddingEnd="4dp"
            android:src="@drawable/ic_work_app_badge"
        android:visibility="invisible"
        app:layout_constraintTop_toBottomOf="@id/guideline"
        app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/screenshot_message_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

        <TextView
            android:id="@+id/screenshot_message_content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
            app:layout_constraintEnd_toStartOf="@id/message_dismiss_button"/>

        <FrameLayout
            android:id="@+id/message_dismiss_button"
            android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
            android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
            app:layout_constraintStart_toEndOf="@id/screenshot_message_content"
        app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:contentDescription="@string/screenshot_dismiss_work_profile">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="@dimen/overlay_dismiss_button_margin"
                android:src="@drawable/overlay_cancel"/>
        </FrameLayout>

        >
    </androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.screenshot.DraggableConstraintLayout>
+41 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <ImageView
        android:id="@+id/screenshot_message_icon"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:paddingEnd="4dp"
        android:src="@drawable/ic_work_app_badge"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/screenshot_message_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <TextView
        android:id="@+id/screenshot_message_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="start"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
        app:layout_constraintEnd_toStartOf="@id/message_dismiss_button"/>

    <FrameLayout
        android:id="@+id/message_dismiss_button"
        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
        app:layout_constraintStart_toEndOf="@id/screenshot_message_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:contentDescription="@string/screenshot_dismiss_work_profile">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/overlay_dismiss_button_margin"
            android:src="@drawable/overlay_cancel"/>
    </FrameLayout>
</merge>
+1 −1
Original line number Diff line number Diff line
@@ -337,7 +337,7 @@
    <!-- Used for both start and bottom margin of the preview, relative to the action container -->
    <dimen name="overlay_preview_container_margin">8dp</dimen>
    <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
    <dimen name="overlay_action_container_margin_bottom">4dp</dimen>
    <dimen name="overlay_action_container_margin_bottom">6dp</dimen>
    <dimen name="overlay_bg_protection_height">242dp</dimen>
    <dimen name="overlay_action_container_corner_radius">18dp</dimen>
    <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
+113 −0
Original line number Diff line number Diff line
package com.android.systemui.screenshot

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.Guideline
import com.android.systemui.R

/**
 * MessageContainerController controls the display of content in the screenshot message container.
 */
class MessageContainerController
constructor(
    parent: ViewGroup,
) {
    private val guideline: Guideline = parent.requireViewById(R.id.guideline)
    private val messageContainer: ViewGroup =
        parent.requireViewById(R.id.screenshot_message_container)

    /**
     * Show a notification under the screenshot view indicating that a work profile screenshot has
     * been taken and which app can be used to view it.
     *
     * @param appName The name of the app to use to view screenshots
     * @param appIcon Optional icon for the relevant files app
     * @param onDismiss Runnable to be run when the user dismisses this message
     */
    fun showWorkProfileMessage(appName: CharSequence, appIcon: Drawable?, onDismiss: Runnable) {
        // Eventually this container will support multiple notification types, but for now just make
        // sure we don't double inflate.
        if (messageContainer.childCount == 0) {
            View.inflate(
                messageContainer.context,
                R.layout.screenshot_work_profile_first_run,
                messageContainer
            )
        }
        if (appIcon != null) {
            // Replace the default icon if one is provided.
            val imageView: ImageView =
                messageContainer.requireViewById<ImageView>(R.id.screenshot_message_icon)
            imageView.setImageDrawable(appIcon)
        }
        val messageContent =
            messageContainer.requireViewById<TextView>(R.id.screenshot_message_content)
        messageContent.text =
            messageContainer.context.getString(
                R.string.screenshot_work_profile_notification,
                appName
            )
        messageContainer.requireViewById<View>(R.id.message_dismiss_button).setOnClickListener {
            animateOutMessageContainer()
            onDismiss.run()
        }

        // Need the container to be fully measured before animating in (to know animation offset
        // destination)
        messageContainer.viewTreeObserver.addOnPreDrawListener(
            object : ViewTreeObserver.OnPreDrawListener {
                override fun onPreDraw(): Boolean {
                    messageContainer.viewTreeObserver.removeOnPreDrawListener(this)
                    animateInMessageContainer()
                    return false
                }
            }
        )
    }

    private fun animateInMessageContainer() {
        if (messageContainer.visibility == View.VISIBLE) return

        messageContainer.visibility = View.VISIBLE
        getAnimator(true).start()
    }

    private fun animateOutMessageContainer() {
        getAnimator(false).apply {
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        super.onAnimationEnd(animation)
                        messageContainer.visibility = View.INVISIBLE
                    }
                }
            )
            start()
        }
    }

    private fun getAnimator(animateIn: Boolean): Animator {
        val params = messageContainer.layoutParams as MarginLayoutParams
        val offset = messageContainer.height + params.topMargin + params.bottomMargin
        val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
        with(anim) {
            duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
            interpolator = AccelerateDecelerateInterpolator()
            addUpdateListener { valueAnimator: ValueAnimator ->
                val interpolation = valueAnimator.animatedValue as Float
                guideline.setGuidelineEnd((interpolation * offset).toInt())
                messageContainer.alpha = interpolation
            }
        }
        return anim
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -293,6 +293,7 @@ public class ScreenshotController {
    };

    private ScreenshotView mScreenshotView;
    private MessageContainerController mMessageContainerController;
    private Bitmap mScreenBitmap;
    private SaveImageInBackgroundTask mSaveInBgTask;
    private boolean mScreenshotTakenInPortrait;
@@ -631,6 +632,7 @@ public class ScreenshotController {
        // Inflate the screenshot layout
        mScreenshotView = (ScreenshotView)
                LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
        mMessageContainerController = new MessageContainerController(mScreenshotView);
        mScreenshotView.addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                    @Override
@@ -1193,7 +1195,8 @@ public class ScreenshotController {
    private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
        mScreenshotView.setChipIntents(imageData);
        if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
            mWorkProfileMessageController.onScreenshotTaken(imageData.owner, mScreenshotView);
            mWorkProfileMessageController.onScreenshotTaken(imageData.owner,
                    mMessageContainerController);
        }
    }

Loading