Changed the transformation from when switching notification views
Change-Id: I2af3c2f36787d208be7745dabae96903df256156
This commit is contained in:
@@ -305,4 +305,13 @@ public class NotificationHeaderView extends LinearLayout {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImageView getExpandButton() {
|
||||
return mExpandButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
|
||||
<!-- For notification icons for which targetSdk < L, this caches whether the icon is grayscale -->
|
||||
<item type="id" name="icon_is_grayscale" />
|
||||
<item type="id" name="clip_children_tag" />
|
||||
<item type="id" name="clip_children_set_tag" />
|
||||
<item type="id" name="clip_to_padding_tag" />
|
||||
<item type="id" name="image_icon_tag" />
|
||||
<item type="id" name="is_clicked_heads_up_tag" />
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
||||
|
||||
/**
|
||||
* A helper to fade views in and out.
|
||||
*/
|
||||
public class CrossFadeHelper {
|
||||
public static final long ANIMATION_DURATION_LENGTH = 210;
|
||||
|
||||
public static void fadeOut(final View view, final Runnable endRunnable) {
|
||||
view.animate().cancel();
|
||||
view.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(ANIMATION_DURATION_LENGTH)
|
||||
.setInterpolator(PhoneStatusBar.ALPHA_OUT)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
if (view.hasOverlappingRendering()) {
|
||||
view.animate().withLayer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void fadeIn(final View view) {
|
||||
view.animate().cancel();
|
||||
if (view.getVisibility() == View.INVISIBLE) {
|
||||
view.setAlpha(0.0f);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
view.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(ANIMATION_DURATION_LENGTH)
|
||||
.setInterpolator(PhoneStatusBar.ALPHA_IN);
|
||||
if (view.hasOverlappingRendering()) {
|
||||
view.animate().withLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -635,6 +635,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mPrivateLayout.updateExpandButtons(isExpandable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClipToActualHeight(boolean clipToActualHeight) {
|
||||
super.setClipToActualHeight(clipToActualHeight);
|
||||
getShowingLayout().setClipToActualHeight(clipToActualHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the user has changed the expansion state
|
||||
*/
|
||||
@@ -1040,7 +1046,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
|
||||
} else {
|
||||
header.reapply(getContext(), mNotificationHeader);
|
||||
mNotificationHeaderWrapper.notifyContentUpdated();
|
||||
mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
|
||||
}
|
||||
updateHeaderExpandButton();
|
||||
updateChildrenHeaderAppearance();
|
||||
|
||||
@@ -45,6 +45,7 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
private static Rect mClipRect = new Rect();
|
||||
private boolean mWillBeGone;
|
||||
private int mMinClipTopAmount = 0;
|
||||
private boolean mClipToActualHeight = true;
|
||||
|
||||
public ExpandableView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -326,12 +327,21 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
}
|
||||
|
||||
private void updateClipping() {
|
||||
int top = mClipTopOptimization;
|
||||
if (top >= getActualHeight()) {
|
||||
top = getActualHeight() - 1;
|
||||
if (mClipToActualHeight) {
|
||||
int top = mClipTopOptimization;
|
||||
if (top >= getActualHeight()) {
|
||||
top = getActualHeight() - 1;
|
||||
}
|
||||
mClipRect.set(0, top, getWidth(), getActualHeight());
|
||||
setClipBounds(mClipRect);
|
||||
} else {
|
||||
setClipBounds(null);
|
||||
}
|
||||
mClipRect.set(0, top, getWidth(), getActualHeight());
|
||||
setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
public void setClipToActualHeight(boolean clipToActualHeight) {
|
||||
mClipToActualHeight = clipToActualHeight;
|
||||
updateClipping();
|
||||
}
|
||||
|
||||
public int getClipTopOptimization() {
|
||||
|
||||
@@ -20,9 +20,6 @@ import android.app.Notification;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.Context;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -32,8 +29,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
@@ -49,7 +44,6 @@ import com.android.systemui.statusbar.policy.RemoteInputView;
|
||||
*/
|
||||
public class NotificationContentView extends FrameLayout {
|
||||
|
||||
private static final long ANIMATION_DURATION_LENGTH = 170;
|
||||
private static final int VISIBLE_TYPE_CONTRACTED = 0;
|
||||
private static final int VISIBLE_TYPE_EXPANDED = 1;
|
||||
private static final int VISIBLE_TYPE_HEADSUP = 2;
|
||||
@@ -57,7 +51,6 @@ public class NotificationContentView extends FrameLayout {
|
||||
|
||||
private final Rect mClipBounds = new Rect();
|
||||
private final int mRoundRectRadius;
|
||||
private final Interpolator mLinearInterpolator = new LinearInterpolator();
|
||||
private final boolean mRoundRectClippingEnabled;
|
||||
private final int mMinContractedHeight;
|
||||
|
||||
@@ -76,7 +69,6 @@ public class NotificationContentView extends FrameLayout {
|
||||
private int mUnrestrictedContentHeight;
|
||||
private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
|
||||
private boolean mDark;
|
||||
private final Paint mFadePaint = new Paint();
|
||||
private boolean mAnimate;
|
||||
private boolean mIsHeadsUp;
|
||||
private boolean mShowingLegacyBackground;
|
||||
@@ -108,11 +100,11 @@ public class NotificationContentView extends FrameLayout {
|
||||
private OnClickListener mExpandClickListener;
|
||||
private boolean mBeforeN;
|
||||
private boolean mExpandable;
|
||||
private boolean mClipToActualHeight = true;
|
||||
|
||||
public NotificationContentView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
|
||||
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
|
||||
mRoundRectRadius = getResources().getDimensionPixelSize(
|
||||
R.dimen.notification_material_rounded_rect_radius);
|
||||
mRoundRectClippingEnabled = getResources().getBoolean(
|
||||
@@ -357,8 +349,17 @@ public class NotificationContentView extends FrameLayout {
|
||||
}
|
||||
|
||||
private void updateClipping() {
|
||||
mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
|
||||
setClipBounds(mClipBounds);
|
||||
if (mClipToActualHeight) {
|
||||
mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
|
||||
setClipBounds(mClipBounds);
|
||||
} else {
|
||||
setClipBounds(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void setClipToActualHeight(boolean clipToActualHeight) {
|
||||
mClipToActualHeight = clipToActualHeight;
|
||||
updateClipping();
|
||||
}
|
||||
|
||||
private void selectLayout(boolean animate, boolean force) {
|
||||
@@ -371,7 +372,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
|| (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
|
||||
|| (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
|
||||
|| visibleType == VISIBLE_TYPE_CONTRACTED)) {
|
||||
runSwitchAnimation(visibleType);
|
||||
animateToVisibleType(visibleType);
|
||||
} else {
|
||||
updateViewVisibilities(visibleType);
|
||||
}
|
||||
@@ -381,57 +382,53 @@ public class NotificationContentView extends FrameLayout {
|
||||
|
||||
private void updateViewVisibilities(int visibleType) {
|
||||
boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
|
||||
mContractedChild.setVisibility(contractedVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
mContractedChild.setAlpha(contractedVisible ? 1f : 0f);
|
||||
mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mContractedWrapper.setVisible(contractedVisible);
|
||||
if (mExpandedChild != null) {
|
||||
boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
|
||||
mExpandedChild.setVisibility(expandedVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
mExpandedChild.setAlpha(expandedVisible ? 1f : 0f);
|
||||
mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mExpandedWrapper.setVisible(expandedVisible);
|
||||
}
|
||||
if (mHeadsUpChild != null) {
|
||||
boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
|
||||
mHeadsUpChild.setVisibility(headsUpVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f);
|
||||
mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mHeadsUpWrapper.setVisible(headsUpVisible);
|
||||
}
|
||||
if (mSingleLineView != null) {
|
||||
boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
|
||||
mSingleLineView.setVisibility(singleLineVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
mSingleLineView.setAlpha(singleLineVisible ? 1f : 0f);
|
||||
mSingleLineView.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mSingleLineView.setVisible(singleLineVisible);
|
||||
}
|
||||
setLayerType(LAYER_TYPE_NONE, null);
|
||||
updateRoundRectClipping();
|
||||
}
|
||||
|
||||
private void runSwitchAnimation(int visibleType) {
|
||||
View shownView = getViewForVisibleType(visibleType);
|
||||
View hiddenView = getViewForVisibleType(mVisibleType);
|
||||
shownView.setVisibility(View.VISIBLE);
|
||||
hiddenView.setVisibility(View.VISIBLE);
|
||||
shownView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
|
||||
hiddenView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
|
||||
setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||
hiddenView.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(ANIMATION_DURATION_LENGTH)
|
||||
.setInterpolator(mLinearInterpolator)
|
||||
.withEndAction(null); // In case we have multiple changes in one frame.
|
||||
shownView.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(ANIMATION_DURATION_LENGTH)
|
||||
.setInterpolator(mLinearInterpolator)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateViewVisibilities(mVisibleType);
|
||||
}
|
||||
});
|
||||
private void animateToVisibleType(int visibleType) {
|
||||
final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
|
||||
final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
|
||||
shownView.transformFrom(hiddenView);
|
||||
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
|
||||
hiddenView.transformTo(shownView, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hiddenView.setVisible(false);
|
||||
}
|
||||
});
|
||||
updateRoundRectClipping();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param visibleType one of the static enum types in this view
|
||||
* @return the corresponding transformable view according to the given visible type
|
||||
*/
|
||||
private TransformableView getTransformableViewForVisibleType(int visibleType) {
|
||||
switch (visibleType) {
|
||||
case VISIBLE_TYPE_EXPANDED:
|
||||
return mExpandedWrapper;
|
||||
case VISIBLE_TYPE_HEADSUP:
|
||||
return mHeadsUpWrapper;
|
||||
case VISIBLE_TYPE_SINGLELINE:
|
||||
return mSingleLineView;
|
||||
default:
|
||||
return mContractedWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param visibleType one of the static enum types in this view
|
||||
* @return the corresponding view according to the given visible type
|
||||
@@ -520,14 +517,14 @@ public class NotificationContentView extends FrameLayout {
|
||||
applyRemoteInput(entry);
|
||||
selectLayout(false /* animate */, true /* force */);
|
||||
if (mContractedChild != null) {
|
||||
mContractedWrapper.notifyContentUpdated();
|
||||
mContractedWrapper.notifyContentUpdated(entry.notification);
|
||||
mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
|
||||
}
|
||||
if (mExpandedChild != null) {
|
||||
mExpandedWrapper.notifyContentUpdated();
|
||||
mExpandedWrapper.notifyContentUpdated(entry.notification);
|
||||
}
|
||||
if (mHeadsUpChild != null) {
|
||||
mHeadsUpWrapper.notifyContentUpdated();
|
||||
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
|
||||
}
|
||||
updateRoundRectClipping();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.NotificationHeaderView;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
@@ -35,6 +37,7 @@ import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.ViewInvertHelper;
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -52,6 +55,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
protected final Interpolator mLinearOutSlowInInterpolator;
|
||||
protected final ViewInvertHelper mInvertHelper;
|
||||
|
||||
protected final ViewTransformationHelper mTransformationHelper;
|
||||
|
||||
protected int mColor;
|
||||
private ImageView mIcon;
|
||||
|
||||
@@ -64,7 +69,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
|
||||
android.R.interpolator.linear_out_slow_in);
|
||||
mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
|
||||
mTransformationHelper = new ViewTransformationHelper();
|
||||
resolveHeaderViews();
|
||||
updateInvertHelper();
|
||||
}
|
||||
|
||||
protected void resolveHeaderViews() {
|
||||
@@ -73,12 +80,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
mColor = resolveColor(mExpandButton);
|
||||
mNotificationHeader = (NotificationHeaderView) mView.findViewById(
|
||||
com.android.internal.R.id.notification_header);
|
||||
for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
|
||||
View child = mNotificationHeader.getChildAt(i);
|
||||
if (child != mIcon) {
|
||||
mInvertHelper.addTarget(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int resolveColor(ImageView icon) {
|
||||
@@ -92,10 +93,26 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyContentUpdated() {
|
||||
mInvertHelper.clearTargets();
|
||||
public void notifyContentUpdated(StatusBarNotification notification) {
|
||||
// Reinspect the notification.
|
||||
resolveHeaderViews();
|
||||
updateInvertHelper();
|
||||
updateTransformedTypes();
|
||||
}
|
||||
|
||||
protected void updateInvertHelper() {
|
||||
mInvertHelper.clearTargets();
|
||||
for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
|
||||
View child = mNotificationHeader.getChildAt(i);
|
||||
if (child != mIcon) {
|
||||
mInvertHelper.addTarget(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateTransformedTypes() {
|
||||
mTransformationHelper.reset();
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_HEADER, mNotificationHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -236,4 +253,25 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
|
||||
public NotificationHeaderView getNotificationHeader() {
|
||||
return mNotificationHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformState getCurrentState(int fadingView) {
|
||||
return mTransformationHelper.getCurrentState(fadingView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, Runnable endRunnable) {
|
||||
mTransformationHelper.transformTo(notification, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
mTransformationHelper.transformFrom(notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
mTransformationHelper.setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,17 @@ package com.android.systemui.statusbar;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.statusbar.notification.ImageTransformState;
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Wraps a notification view inflated from a template.
|
||||
@@ -32,15 +40,21 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
|
||||
|
||||
protected ImageView mPicture;
|
||||
private ProgressBar mProgressBar;
|
||||
private TextView mTitle;
|
||||
private TextView mText;
|
||||
|
||||
protected NotificationTemplateViewWrapper(Context ctx, View view) {
|
||||
super(ctx, view);
|
||||
resolveTemplateViews();
|
||||
}
|
||||
|
||||
private void resolveTemplateViews() {
|
||||
View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
|
||||
private void resolveTemplateViews(StatusBarNotification notification) {
|
||||
mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
|
||||
if (notification != null) {
|
||||
mPicture.setTag(ImageTransformState.ICON_TAG,
|
||||
notification.getNotification().getLargeIcon());
|
||||
}
|
||||
mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
|
||||
mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
|
||||
final View progress = mView.findViewById(com.android.internal.R.id.progress);
|
||||
if (progress instanceof ProgressBar) {
|
||||
mProgressBar = (ProgressBar) progress;
|
||||
@@ -48,17 +62,50 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
|
||||
// It's still a viewstub
|
||||
mProgressBar = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyContentUpdated(StatusBarNotification notification) {
|
||||
// Reinspect the notification.
|
||||
resolveTemplateViews(notification);
|
||||
super.notifyContentUpdated(notification);
|
||||
addRemainingTransformTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
|
||||
* child is faded automatically and doesn't have to be manually added.
|
||||
* The keys used for the views are the ids.
|
||||
*/
|
||||
private void addRemainingTransformTypes() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateInvertHelper() {
|
||||
super.updateInvertHelper();
|
||||
View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
|
||||
if (mainColumn != null) {
|
||||
mInvertHelper.addTarget(mainColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyContentUpdated() {
|
||||
super.notifyContentUpdated();
|
||||
|
||||
// Reinspect the notification.
|
||||
resolveTemplateViews();
|
||||
protected void updateTransformedTypes() {
|
||||
// This also clears the existing types
|
||||
super.updateTransformedTypes();
|
||||
if (mTitle != null) {
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, mTitle);
|
||||
}
|
||||
if (mText != null) {
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mText);
|
||||
}
|
||||
if (mPicture != null) {
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_IMAGE, mPicture);
|
||||
}
|
||||
if (mProgressBar != null) {
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_PROGRESS, mProgressBar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,14 +17,17 @@
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.view.NotificationHeaderView;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
|
||||
/**
|
||||
* Wraps the actual notification content view; used to implement behaviors which are different for
|
||||
* the individual templates and custom views.
|
||||
*/
|
||||
public abstract class NotificationViewWrapper {
|
||||
public abstract class NotificationViewWrapper implements TransformableView {
|
||||
|
||||
protected final View mView;
|
||||
|
||||
@@ -53,8 +56,9 @@ public abstract class NotificationViewWrapper {
|
||||
|
||||
/**
|
||||
* Notifies this wrapper that the content of the view might have changed.
|
||||
* @param notification
|
||||
*/
|
||||
public void notifyContentUpdated() {};
|
||||
public void notifyContentUpdated(StatusBarNotification notification) {};
|
||||
|
||||
/**
|
||||
* @return true if this template might need to be clipped with a round rect to make it look
|
||||
@@ -78,4 +82,26 @@ public abstract class NotificationViewWrapper {
|
||||
public NotificationHeaderView getNotificationHeader() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformState getCurrentState(int fadingView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, Runnable endRunnable) {
|
||||
// By default we are fading out completely
|
||||
CrossFadeHelper.fadeOut(mView, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
// By default we are fading in completely
|
||||
CrossFadeHelper.fadeIn(mView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar;
|
||||
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
|
||||
/**
|
||||
* A view that can be transformed to and from.
|
||||
*/
|
||||
public interface TransformableView {
|
||||
int TRANSFORMING_VIEW_HEADER = 0;
|
||||
int TRANSFORMING_VIEW_TITLE = 1;
|
||||
int TRANSFORMING_VIEW_TEXT = 2;
|
||||
int TRANSFORMING_VIEW_IMAGE = 3;
|
||||
int TRANSFORMING_VIEW_PROGRESS = 4;
|
||||
|
||||
/**
|
||||
* Get the current state of a view in a transform animation
|
||||
* @param fadingView which view we are interested in
|
||||
* @return the current transform state of this viewtype
|
||||
*/
|
||||
TransformState getCurrentState(int fadingView);
|
||||
|
||||
/**
|
||||
* Transform to the given view
|
||||
* @param notification the view to transform to
|
||||
*/
|
||||
void transformTo(TransformableView notification, Runnable endRunnable);
|
||||
|
||||
/**
|
||||
* Transform to this view from the given view
|
||||
* @param notification the view to transform from
|
||||
*/
|
||||
void transformFrom(TransformableView notification);
|
||||
|
||||
/**
|
||||
* Set this view to be fully visible or gone
|
||||
* @param visible
|
||||
*/
|
||||
void setVisible(boolean visible);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.notification.TransformState;
|
||||
|
||||
/**
|
||||
* A view that can be transformed to and from.
|
||||
*/
|
||||
public class ViewTransformationHelper implements TransformableView {
|
||||
private final Handler mHandler = new Handler();
|
||||
private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
|
||||
|
||||
public void addTransformedView(int key, View transformedView) {
|
||||
mTransformedViews.put(key, transformedView);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mTransformedViews.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformState getCurrentState(int fadingView) {
|
||||
View view = mTransformedViews.get(fadingView);
|
||||
if (view != null && view.getVisibility() != View.GONE) {
|
||||
return TransformState.createFrom(view);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, Runnable endRunnable) {
|
||||
Runnable runnable = endRunnable;
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
TransformState otherState = notification.getCurrentState(viewType);
|
||||
if (otherState != null) {
|
||||
boolean run = ownState.transformViewTo(otherState, runnable);
|
||||
otherState.recycle();
|
||||
if (run) {
|
||||
runnable = null;
|
||||
}
|
||||
} else {
|
||||
// there's no other view available
|
||||
CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), runnable);
|
||||
runnable = null;
|
||||
}
|
||||
ownState.recycle();
|
||||
}
|
||||
}
|
||||
if (runnable != null) {
|
||||
// We need to post, since the visible type is only set after the transformation is
|
||||
// started
|
||||
mHandler.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
TransformState otherState = notification.getCurrentState(viewType);
|
||||
if (otherState != null) {
|
||||
ownState.transformViewFrom(otherState);
|
||||
otherState.recycle();
|
||||
} else {
|
||||
// There's no other view, lets fade us in
|
||||
// Certain views need to prepare the fade in and make sure its children are
|
||||
// completely visible. An example is the notification header.
|
||||
ownState.prepareFadeIn();
|
||||
CrossFadeHelper.fadeIn(mTransformedViews.get(viewType));
|
||||
}
|
||||
ownState.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
for (Integer viewType : mTransformedViews.keySet()) {
|
||||
TransformState ownState = getCurrentState(viewType);
|
||||
if (ownState != null) {
|
||||
ownState.setVisible(visible);
|
||||
ownState.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar.notification;
|
||||
|
||||
import android.util.Pools;
|
||||
import android.view.NotificationHeaderView;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.statusbar.CrossFadeHelper;
|
||||
|
||||
/**
|
||||
* A transform state of a text view.
|
||||
*/
|
||||
public class HeaderTransformState extends TransformState {
|
||||
|
||||
private static Pools.SimplePool<HeaderTransformState> sInstancePool
|
||||
= new Pools.SimplePool<>(40);
|
||||
private View mExpandButton;
|
||||
|
||||
@Override
|
||||
public void initFrom(View view) {
|
||||
super.initFrom(view);
|
||||
if (view instanceof NotificationHeaderView) {
|
||||
NotificationHeaderView header = (NotificationHeaderView) view;
|
||||
mExpandButton = header.getExpandButton();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformViewTo(TransformState otherState, Runnable endRunnable) {
|
||||
// if the transforming notification has a header, we have ensured that it looks the same
|
||||
// but the expand button, so lets fade just that one.
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
return false;
|
||||
}
|
||||
NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
|
||||
int childCount = header.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View headerChild = header.getChildAt(i);
|
||||
if (headerChild.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
if (headerChild != mExpandButton) {
|
||||
headerChild.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
CrossFadeHelper.fadeOut(mExpandButton, endRunnable);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformViewFrom(TransformState otherState) {
|
||||
// if the transforming notification has a header, we have ensured that it looks the same
|
||||
// but the expand button, so lets fade just that one.
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
return;
|
||||
}
|
||||
NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
|
||||
header.setVisibility(View.VISIBLE);
|
||||
header.setAlpha(1.0f);
|
||||
int childCount = header.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View headerChild = header.getChildAt(i);
|
||||
if (headerChild.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
if (headerChild != mExpandButton) {
|
||||
headerChild.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
CrossFadeHelper.fadeIn(mExpandButton);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public static HeaderTransformState obtain() {
|
||||
HeaderTransformState instance = sInstancePool.acquire();
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
return new HeaderTransformState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
super.reset();
|
||||
mExpandButton = null;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
return;
|
||||
}
|
||||
NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
|
||||
int childCount = header.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View headerChild = header.getChildAt(i);
|
||||
if (headerChild.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
headerChild.animate().cancel();
|
||||
headerChild.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
if (headerChild == mExpandButton) {
|
||||
headerChild.setAlpha(visible ? 1.0f : 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFadeIn() {
|
||||
super.prepareFadeIn();
|
||||
if (!(mTransformedView instanceof NotificationHeaderView)) {
|
||||
return;
|
||||
}
|
||||
NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
|
||||
int childCount = header.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View headerChild = header.getChildAt(i);
|
||||
if (headerChild.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
headerChild.animate().cancel();
|
||||
headerChild.setVisibility(View.VISIBLE);
|
||||
headerChild.setAlpha(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,28 @@ package com.android.systemui.statusbar.notification;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.keyguard.AlphaOptimizedLinearLayout;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.ViewInvertHelper;
|
||||
import com.android.systemui.statusbar.TransformableView;
|
||||
import com.android.systemui.statusbar.ViewTransformationHelper;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A hybrid view which may contain information about one ore more notifications.
|
||||
*/
|
||||
public class HybridNotificationView extends AlphaOptimizedLinearLayout {
|
||||
public class HybridNotificationView extends AlphaOptimizedLinearLayout
|
||||
implements TransformableView {
|
||||
|
||||
private ViewTransformationHelper mTransformationHelper;
|
||||
|
||||
protected TextView mTitleView;
|
||||
protected TextView mTextView;
|
||||
@@ -58,6 +68,9 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout {
|
||||
mTitleView = (TextView) findViewById(R.id.notification_title);
|
||||
mTextView = (TextView) findViewById(R.id.notification_text);
|
||||
mInvertHelper = new ViewInvertHelper(this, NotificationPanelView.DOZE_ANIMATION_DURATION);
|
||||
mTransformationHelper = new ViewTransformationHelper();
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, mTitleView);
|
||||
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView);
|
||||
}
|
||||
|
||||
public void bind(CharSequence title) {
|
||||
@@ -66,11 +79,38 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout {
|
||||
|
||||
public void bind(CharSequence title, CharSequence text) {
|
||||
mTitleView.setText(title);
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
mTitleView.setVisibility(GONE);
|
||||
}
|
||||
mTextView.setText(text);
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
mTextView.setVisibility(GONE);
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
mInvertHelper.setInverted(dark, fade, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformState getCurrentState(int fadingView) {
|
||||
return mTransformationHelper.getCurrentState(fadingView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformTo(TransformableView notification, Runnable endRunnable) {
|
||||
mTransformationHelper.transformTo(notification, endRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformFrom(TransformableView notification) {
|
||||
mTransformationHelper.transformFrom(notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
mTransformationHelper.setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar.notification;
|
||||
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.util.Pools;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* A transform state of a image view.
|
||||
*/
|
||||
public class ImageTransformState extends TransformState {
|
||||
|
||||
public static final int ICON_TAG = R.id.image_icon_tag;
|
||||
private static Pools.SimplePool<ImageTransformState> sInstancePool
|
||||
= new Pools.SimplePool<>(40);
|
||||
private Icon mIcon;
|
||||
|
||||
@Override
|
||||
public void initFrom(View view) {
|
||||
super.initFrom(view);
|
||||
if (view instanceof ImageView) {
|
||||
mIcon = (Icon) view.getTag(ICON_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sameAs(TransformState otherState) {
|
||||
if (otherState instanceof ImageTransformState) {
|
||||
return mIcon.sameAs(((ImageTransformState) otherState).getIcon());
|
||||
}
|
||||
return super.sameAs(otherState);
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
public static ImageTransformState obtain() {
|
||||
ImageTransformState instance = sInstancePool.acquire();
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
return new ImageTransformState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
super.reset();
|
||||
mIcon = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar.notification;
|
||||
|
||||
import android.util.Pools;
|
||||
|
||||
/**
|
||||
* A transform state of a progress view.
|
||||
*/
|
||||
public class ProgressTransformState extends TransformState {
|
||||
|
||||
private static Pools.SimplePool<ProgressTransformState> sInstancePool
|
||||
= new Pools.SimplePool<>(40);
|
||||
|
||||
@Override
|
||||
protected boolean sameAs(TransformState otherState) {
|
||||
if (otherState instanceof ProgressTransformState) {
|
||||
return true;
|
||||
}
|
||||
return super.sameAs(otherState);
|
||||
}
|
||||
|
||||
public static ProgressTransformState obtain() {
|
||||
ProgressTransformState instance = sInstancePool.acquire();
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
return new ProgressTransformState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar.notification;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pools;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A transform state of a mText view.
|
||||
*/
|
||||
public class TextViewTransformState extends TransformState {
|
||||
|
||||
private static Pools.SimplePool<TextViewTransformState> sInstancePool
|
||||
= new Pools.SimplePool<>(40);
|
||||
private CharSequence mText;
|
||||
|
||||
@Override
|
||||
public void initFrom(View view) {
|
||||
super.initFrom(view);
|
||||
if (view instanceof TextView) {
|
||||
TextView txt = (TextView) view;
|
||||
mText = txt.getText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sameAs(TransformState otherState) {
|
||||
if (otherState instanceof TextViewTransformState) {
|
||||
TextViewTransformState otherTvs = (TextViewTransformState) otherState;
|
||||
return TextUtils.equals(otherTvs.mText, mText);
|
||||
}
|
||||
return super.sameAs(otherState);
|
||||
}
|
||||
|
||||
public static TextViewTransformState obtain() {
|
||||
TextViewTransformState instance = sInstancePool.acquire();
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
return new TextViewTransformState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
super.reset();
|
||||
mText = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.statusbar.notification;
|
||||
|
||||
import android.util.ArraySet;
|
||||
import android.view.NotificationHeaderView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.CrossFadeHelper;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
|
||||
/**
|
||||
* A transform state of a view.
|
||||
*/
|
||||
public abstract class TransformState {
|
||||
|
||||
private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
|
||||
private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
|
||||
private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
|
||||
protected View mTransformedView;
|
||||
private int[] mOwnPosition = new int[2];
|
||||
|
||||
public void initFrom(View view) {
|
||||
mTransformedView = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the {@link #mTransformedView} from the given transformviewstate
|
||||
* @param otherState the state to transform from
|
||||
*/
|
||||
public void transformViewFrom(TransformState otherState) {
|
||||
mTransformedView.animate().cancel();
|
||||
if (sameAs(otherState)) {
|
||||
// We have the same content, lets show ourselves
|
||||
mTransformedView.setAlpha(1.0f);
|
||||
mTransformedView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
CrossFadeHelper.fadeIn(mTransformedView);
|
||||
}
|
||||
|
||||
// lets animate the positions correctly
|
||||
int[] otherPosition = otherState.getLocationOnScreen();
|
||||
int[] ownStablePosition = getLaidOutLocationOnScreen();
|
||||
mTransformedView.setTranslationX(otherPosition[0] - ownStablePosition[0]);
|
||||
mTransformedView.setTranslationY(otherPosition[1] - ownStablePosition[1]);
|
||||
mTransformedView.animate()
|
||||
.translationX(0)
|
||||
.translationY(0)
|
||||
.setDuration(CrossFadeHelper.ANIMATION_DURATION_LENGTH)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setClippingDeactivated(false);
|
||||
}
|
||||
});
|
||||
setClippingDeactivated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the {@link #mTransformedView} to the given transformviewstate
|
||||
* @param otherState the state to transform from
|
||||
* @param endRunnable a runnable to run at the end of the animation
|
||||
* @return whether an animation was started
|
||||
*/
|
||||
public boolean transformViewTo(TransformState otherState, final Runnable endRunnable) {
|
||||
mTransformedView.animate().cancel();
|
||||
if (sameAs(otherState)) {
|
||||
// We have the same text, lets show ourselfs
|
||||
mTransformedView.setAlpha(0.0f);
|
||||
mTransformedView.setVisibility(View.INVISIBLE);
|
||||
return false;
|
||||
} else {
|
||||
CrossFadeHelper.fadeOut(mTransformedView, endRunnable);
|
||||
}
|
||||
// lets animate the positions correctly
|
||||
int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
|
||||
int[] ownPosition = getLaidOutLocationOnScreen();
|
||||
mTransformedView.animate()
|
||||
.translationX(otherStablePosition[0] - ownPosition[0])
|
||||
.translationY(otherStablePosition[1] - ownPosition[1])
|
||||
.setDuration(CrossFadeHelper.ANIMATION_DURATION_LENGTH)
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
setClippingDeactivated(false);
|
||||
}
|
||||
});
|
||||
setClippingDeactivated(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setClippingDeactivated(boolean deactivated) {
|
||||
ViewGroup view = (ViewGroup) mTransformedView.getParent();
|
||||
while (true) {
|
||||
ArraySet<View> clipSet = (ArraySet<View>) view.getTag(CLIP_CLIPPING_SET);
|
||||
if (clipSet == null) {
|
||||
clipSet = new ArraySet<>();
|
||||
view.setTag(CLIP_CLIPPING_SET, clipSet);
|
||||
}
|
||||
Boolean clipChildren = (Boolean) view.getTag(CLIP_CHILDREN_TAG);
|
||||
if (clipChildren == null) {
|
||||
clipChildren = view.getClipChildren();
|
||||
view.setTag(CLIP_CHILDREN_TAG, clipChildren);
|
||||
}
|
||||
Boolean clipToPadding = (Boolean) view.getTag(CLIP_TO_PADDING);
|
||||
if (clipToPadding == null) {
|
||||
clipToPadding = view.getClipToPadding();
|
||||
view.setTag(CLIP_TO_PADDING, clipToPadding);
|
||||
}
|
||||
ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
|
||||
? (ExpandableNotificationRow) view
|
||||
: null;
|
||||
if (!deactivated) {
|
||||
clipSet.remove(mTransformedView);
|
||||
if (clipSet.isEmpty()) {
|
||||
view.setClipChildren(clipChildren);
|
||||
view.setClipToPadding(clipToPadding);
|
||||
view.setTag(CLIP_CLIPPING_SET, null);
|
||||
if (row != null) {
|
||||
row.setClipToActualHeight(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clipSet.add(mTransformedView);
|
||||
view.setClipChildren(false);
|
||||
view.setClipToPadding(false);
|
||||
if (row != null && row.isChildInGroup()) {
|
||||
// We still want to clip to the parent's height
|
||||
row.setClipToActualHeight(false);
|
||||
}
|
||||
}
|
||||
if (row != null && !row.isChildInGroup()) {
|
||||
return;
|
||||
}
|
||||
final ViewParent parent = view.getParent();
|
||||
if (parent instanceof ViewGroup) {
|
||||
view = (ViewGroup) parent;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getLaidOutLocationOnScreen() {
|
||||
int[] location = getLocationOnScreen();
|
||||
location[0] -= mTransformedView.getTranslationX();
|
||||
location[1] -= mTransformedView.getTranslationY();
|
||||
return location;
|
||||
}
|
||||
|
||||
private int[] getLocationOnScreen() {
|
||||
mTransformedView.getLocationOnScreen(mOwnPosition);
|
||||
return mOwnPosition;
|
||||
}
|
||||
|
||||
protected boolean sameAs(TransformState otherState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TransformState createFrom(View view) {
|
||||
if (view instanceof TextView) {
|
||||
TextViewTransformState result = TextViewTransformState.obtain();
|
||||
result.initFrom(view);
|
||||
return result;
|
||||
}
|
||||
if (view instanceof NotificationHeaderView) {
|
||||
HeaderTransformState result = HeaderTransformState.obtain();
|
||||
result.initFrom(view);
|
||||
return result;
|
||||
}
|
||||
if (view instanceof ImageView) {
|
||||
ImageTransformState result = ImageTransformState.obtain();
|
||||
result.initFrom(view);
|
||||
return result;
|
||||
}
|
||||
if (view instanceof ProgressBar) {
|
||||
ProgressTransformState result = ProgressTransformState.obtain();
|
||||
result.initFrom(view);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
reset();
|
||||
}
|
||||
|
||||
protected void reset() {
|
||||
mTransformedView = null;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
mTransformedView.animate().cancel();
|
||||
mTransformedView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
mTransformedView.setAlpha(visible ? 1.0f : 0.0f);
|
||||
if (visible) {
|
||||
mTransformedView.setTranslationX(0);
|
||||
mTransformedView.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void prepareFadeIn() {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user