blob: 63f59be3165199a948d35160f85ed3ef8ac034cb [file] [log] [blame]
/*
* Copyright (C) 2014 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.recents.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.recents.RecentsConfiguration;
/**
* The full screen transition view that gets animated down from the full screen into a task
* thumbnail view.
*/
public class FullscreenTransitionOverlayView extends FrameLayout {
/** The FullscreenTransitionOverlayView callbacks */
public interface FullScreenTransitionViewCallbacks {
void onEnterAnimationComplete();
}
RecentsConfiguration mConfig;
FullScreenTransitionViewCallbacks mCb;
ImageView mScreenshotView;
Rect mClipRect = new Rect();
Paint mLayerPaint = new Paint();
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
boolean mIsAnimating;
AnimatorSet mEnterAnimation;
public FullscreenTransitionOverlayView(Context context) {
super(context);
}
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public FullscreenTransitionOverlayView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mMaxDim = mConfig.taskStackMaxDim;
setClipTop(getClipTop());
setClipBottom(getClipBottom());
setDim(getDim());
setWillNotDraw(false);
}
@Override
protected void onFinishInflate() {
mScreenshotView = (ImageView) findViewById(R.id.image);
}
/** Sets the callbacks */
public void setCallbacks(FullScreenTransitionViewCallbacks cb) {
mCb = cb;
}
/** Sets the top clip */
public void setClipTop(int clip) {
mClipRect.top = clip;
setClipBounds(mClipRect);
}
/** Gets the top clip */
public int getClipTop() {
return mClipRect.top;
}
/** Sets the bottom clip */
public void setClipBottom(int clip) {
mClipRect.bottom = clip;
setClipBounds(mClipRect);
}
/** Gets the top clip */
public int getClipBottom() {
return mClipRect.bottom;
}
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
/*
int inverse = 255 - mDim;
mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
mLayerPaint.setColorFilter(mDimColorFilter);
setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
*/
}
/** Returns the current dim. */
public int getDim() {
return mDim;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
@Override
public boolean hasOverlappingRendering() {
return false;
}
/** Prepares the screenshot view for the transition into Recents */
public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
if (!mConfig.launchedFromAppWithScreenshot) return;
setClipTop(0);
setClipBottom(getMeasuredHeight());
setDim(0);
setTranslationY(0f);
setScaleX(1f);
setScaleY(1f);
setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
if (screenshot != null) {
mScreenshotView.setImageBitmap(screenshot);
} else {
mScreenshotView.setImageDrawable(null);
}
}
/** Resets the transition view */
public void reset() {
setVisibility(View.GONE);
mScreenshotView.setImageDrawable(null);
}
/** Animates this view as it enters recents */
public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
final Runnable postAnimRunnable) {
// Cancel the current animation
if (mEnterAnimation != null) {
mEnterAnimation.removeAllListeners();
mEnterAnimation.cancel();
}
// Calculate the bottom clip
Rect targetTaskRect = ctx.targetTaskTransform.rect;
float scale = (float) targetTaskRect.width() / getMeasuredWidth();
float scaleYOffset = ((1f - scale) * getMeasuredHeight()) / 2;
float scaledTopInset = (int) (scale * mConfig.systemInsets.top);
int translationY = (int) -scaleYOffset + (int) (mConfig.systemInsets.top - scaledTopInset)
+ targetTaskRect.top;
int clipBottom = mConfig.systemInsets.top + (int) (targetTaskRect.height() / scale);
// Calculate the dim
float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
float scaleRange = 1f - minScale;
float dim = (1f - ctx.targetTaskTransform.scale) / scaleRange;
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
int toDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
// Enable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
// Compose the animation
mEnterAnimation = new AnimatorSet();
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
post(new Runnable() {
@Override
public void run() {
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on this view
setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
// Notify any callbacks
mCb.onEnterAnimationComplete();
// Run the given post-anim runnable
postAnimRunnable.run();
}
});
}
});
// XXX: Translation y should be negative initially to simulate moving from the top of the screen?
mEnterAnimation.setStartDelay(0);
mEnterAnimation.setDuration(475);
mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
mEnterAnimation.playTogether(
// ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
ObjectAnimator.ofInt(this, "dim", toDim),
ObjectAnimator.ofFloat(this, "translationY", translationY),
ObjectAnimator.ofFloat(this, "scaleX", scale),
ObjectAnimator.ofFloat(this, "scaleY", scale)
);
setClipTop(mConfig.systemInsets.top);
mEnterAnimation.start();
mIsAnimating = true;
}
/** Animates this view back out of Recents if we were in the process of animating in. */
public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
if (mIsAnimating) {
// Cancel the current animation
if (mEnterAnimation != null) {
mEnterAnimation.removeAllListeners();
mEnterAnimation.cancel();
}
// Enable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
// Compose the animation
mEnterAnimation = new AnimatorSet();
mEnterAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
post(new Runnable() {
@Override
public void run() {
// Mark that we are no longer animating
mIsAnimating = false;
// Disable the HW Layers on the screenshot view
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
// Notify any callbacks
mCb.onEnterAnimationComplete();
// Run the given post-anim runnable
postAnimRunnable.run();
}
});
}
});
mEnterAnimation.setDuration(475);
mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
mEnterAnimation.playTogether(
ObjectAnimator.ofInt(this, "clipTop", 0),
ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
ObjectAnimator.ofInt(this, "dim", 0),
ObjectAnimator.ofFloat(this, "translationY", 0f),
ObjectAnimator.ofFloat(this, "scaleX", 1f),
ObjectAnimator.ofFloat(this, "scaleY", 1f)
);
mEnterAnimation.start();
return true;
}
return false;
}
}