am bf1960ce: Merge "Fade notifications when exiting dozing" into lmp-mr1-dev
automerge: 6456513
* commit '6456513f6ff844fd15f3453a66fcc8fb439e1ef7':
Fade notifications when exiting dozing
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
android:layout_height="@dimen/notification_large_icon_height"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="top"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
android:layout_height="@dimen/notification_large_icon_height"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
android:layout_height="@dimen/notification_large_icon_height"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
android:layout_height="@dimen/notification_large_icon_height"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
<java-symbol type="id" name="status_bar_latest_event_content" />
|
||||
<java-symbol type="id" name="action_divider" />
|
||||
<java-symbol type="id" name="overflow_divider" />
|
||||
<java-symbol type="id" name="notification_main_column" />
|
||||
<java-symbol type="id" name="sms_short_code_confirm_message" />
|
||||
<java-symbol type="id" name="sms_short_code_detail_layout" />
|
||||
<java-symbol type="id" name="sms_short_code_detail_message" />
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
|
||||
101
packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
Normal file
101
packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
/**
|
||||
* Helper to invert the colors of views and fade between the states.
|
||||
*/
|
||||
public class ViewInvertHelper {
|
||||
|
||||
private final Paint mDarkPaint = new Paint();
|
||||
private final Interpolator mLinearOutSlowInInterpolator;
|
||||
private final View mTarget;
|
||||
private final ColorMatrix mMatrix = new ColorMatrix();
|
||||
private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
|
||||
private final long mFadeDuration;
|
||||
|
||||
public ViewInvertHelper(View target, long fadeDuration) {
|
||||
mTarget = target;
|
||||
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(),
|
||||
android.R.interpolator.linear_out_slow_in);
|
||||
mFadeDuration = fadeDuration;
|
||||
}
|
||||
|
||||
public void fade(final boolean invert, long delay) {
|
||||
float startIntensity = invert ? 0f : 1f;
|
||||
float endIntensity = invert ? 1f : 0f;
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateInvertPaint((Float) animation.getAnimatedValue());
|
||||
mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
|
||||
}
|
||||
});
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!invert) {
|
||||
mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
animator.setDuration(mFadeDuration);
|
||||
animator.setInterpolator(mLinearOutSlowInInterpolator);
|
||||
animator.setStartDelay(delay);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public void update(boolean invert) {
|
||||
if (invert) {
|
||||
updateInvertPaint(1f);
|
||||
mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
|
||||
} else {
|
||||
mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
}
|
||||
|
||||
public View getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
private void updateInvertPaint(float intensity) {
|
||||
float components = 1 - 2 * intensity;
|
||||
final float[] invert = {
|
||||
components, 0f, 0f, 0f, 255f * intensity,
|
||||
0f, components, 0f, 0f, 255f * intensity,
|
||||
0f, 0f, components, 0f, 255f * intensity,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
};
|
||||
mMatrix.set(invert);
|
||||
mGrayscaleMatrix.setSaturation(1 - intensity);
|
||||
mMatrix.preConcat(mGrayscaleMatrix);
|
||||
mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
/**
|
||||
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
|
||||
@@ -53,6 +54,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
private static final long DOUBLETAP_TIMEOUT_MS = 1200;
|
||||
private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
|
||||
private static final int ACTIVATE_ANIMATION_LENGTH = 220;
|
||||
private static final int DARK_ANIMATION_LENGTH = 170;
|
||||
|
||||
/**
|
||||
* The amount of width, which is kept in the end when performing a disappear animation (also
|
||||
@@ -84,6 +86,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
*/
|
||||
private static final float VERTICAL_ANIMATION_START = 1.0f;
|
||||
|
||||
/**
|
||||
* Scale for the background to animate from when exiting dark mode.
|
||||
*/
|
||||
private static final float DARK_EXIT_SCALE_START = 0.93f;
|
||||
|
||||
private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
|
||||
= new PathInterpolator(0.6f, 0, 0.5f, 1);
|
||||
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
|
||||
@@ -94,7 +101,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
|
||||
private boolean mDimmed;
|
||||
private boolean mDark;
|
||||
private final Paint mDarkPaint = createDarkPaint();
|
||||
|
||||
private int mBgTint = 0;
|
||||
private final int mRoundedRectCornerRadius;
|
||||
@@ -332,40 +338,32 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
if (mDimmed != dimmed) {
|
||||
mDimmed = dimmed;
|
||||
if (fade) {
|
||||
fadeBackground();
|
||||
fadeDimmedBackground();
|
||||
} else {
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setDark(boolean dark, boolean fade) {
|
||||
// TODO implement fade
|
||||
if (mDark != dark) {
|
||||
mDark = dark;
|
||||
if (mDark) {
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
|
||||
} else {
|
||||
setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
super.setDark(dark, fade, delay);
|
||||
if (mDark == dark) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static Paint createDarkPaint() {
|
||||
final Paint p = new Paint();
|
||||
final float[] invert = {
|
||||
-1f, 0f, 0f, 1f, 1f,
|
||||
0f, -1f, 0f, 1f, 1f,
|
||||
0f, 0f, -1f, 1f, 1f,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
};
|
||||
final ColorMatrix m = new ColorMatrix(invert);
|
||||
final ColorMatrix grayscale = new ColorMatrix();
|
||||
grayscale.setSaturation(0);
|
||||
m.preConcat(grayscale);
|
||||
p.setColorFilter(new ColorMatrixColorFilter(m));
|
||||
return p;
|
||||
}
|
||||
mDark = dark;
|
||||
if (!dark && fade) {
|
||||
if (mActivated) {
|
||||
mBackgroundDimmed.setVisibility(View.VISIBLE);
|
||||
mBackgroundNormal.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mBackgroundDimmed.setVisibility(View.VISIBLE);
|
||||
mBackgroundNormal.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
fadeDarkToDimmed(delay);
|
||||
} else {
|
||||
updateBackground();
|
||||
}
|
||||
}
|
||||
|
||||
public void setShowingLegacyBackground(boolean showing) {
|
||||
mShowingLegacyBackground = showing;
|
||||
@@ -402,7 +400,39 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
mBackgroundNormal.setRippleColor(rippleColor);
|
||||
}
|
||||
|
||||
private void fadeBackground() {
|
||||
/**
|
||||
* Fades the dimmed background when exiting dark mode.
|
||||
*/
|
||||
private void fadeDarkToDimmed(long delay) {
|
||||
mBackgroundDimmed.setAlpha(0f);
|
||||
mBackgroundDimmed.setPivotX(mBackgroundDimmed.getWidth() / 2f);
|
||||
mBackgroundDimmed.setPivotY(getActualHeight() / 2f);
|
||||
mBackgroundDimmed.setScaleX(DARK_EXIT_SCALE_START);
|
||||
mBackgroundDimmed.setScaleY(DARK_EXIT_SCALE_START);
|
||||
mBackgroundDimmed.animate()
|
||||
.alpha(1f)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(DARK_ANIMATION_LENGTH)
|
||||
.setStartDelay(delay)
|
||||
.setInterpolator(mLinearOutSlowInInterpolator)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
// Jump state if we are cancelled
|
||||
mBackgroundDimmed.setScaleX(1f);
|
||||
mBackgroundDimmed.setScaleY(1f);
|
||||
mBackgroundDimmed.setAlpha(1f);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades the background when the dimmed state changes.
|
||||
*/
|
||||
private void fadeDimmedBackground() {
|
||||
mBackgroundDimmed.animate().cancel();
|
||||
mBackgroundNormal.animate().cancel();
|
||||
if (mDimmed) {
|
||||
mBackgroundDimmed.setVisibility(View.VISIBLE);
|
||||
@@ -443,11 +473,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
}
|
||||
|
||||
private void updateBackground() {
|
||||
if (mDimmed) {
|
||||
cancelFadeAnimations();
|
||||
if (mDark) {
|
||||
mBackgroundDimmed.setVisibility(View.INVISIBLE);
|
||||
mBackgroundNormal.setVisibility(View.INVISIBLE);
|
||||
} else if (mDimmed) {
|
||||
mBackgroundDimmed.setVisibility(View.VISIBLE);
|
||||
mBackgroundNormal.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
cancelFadeAnimations();
|
||||
mBackgroundDimmed.setVisibility(View.INVISIBLE);
|
||||
mBackgroundNormal.setVisibility(View.VISIBLE);
|
||||
mBackgroundNormal.setAlpha(1f);
|
||||
@@ -459,6 +492,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
if (mBackgroundAnimator != null) {
|
||||
mBackgroundAnimator.cancel();
|
||||
}
|
||||
mBackgroundDimmed.animate().cancel();
|
||||
mBackgroundNormal.animate().cancel();
|
||||
}
|
||||
|
||||
|
||||
@@ -207,11 +207,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDark(boolean dark, boolean fade) {
|
||||
super.setDark(dark, fade);
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
super.setDark(dark, fade, delay);
|
||||
final NotificationContentView showing = getShowingLayout();
|
||||
if (showing != null) {
|
||||
showing.setDark(dark, fade);
|
||||
showing.setDark(dark, fade, delay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
private int mActualHeight;
|
||||
protected int mClipTopAmount;
|
||||
private boolean mActualHeightInitialized;
|
||||
private boolean mDark;
|
||||
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
|
||||
|
||||
public ExpandableView(Context context, AttributeSet attrs) {
|
||||
@@ -185,8 +186,14 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
*
|
||||
* @param dark Whether the notification should be dark.
|
||||
* @param fade Whether an animation should be played to change the state.
|
||||
* @param delay If fading, the delay of the animation.
|
||||
*/
|
||||
public void setDark(boolean dark, boolean fade) {
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
mDark = dark;
|
||||
}
|
||||
|
||||
public boolean isDark() {
|
||||
return mDark;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,14 +42,14 @@ import com.android.systemui.R;
|
||||
public class NotificationContentView extends FrameLayout {
|
||||
|
||||
private static final long ANIMATION_DURATION_LENGTH = 170;
|
||||
private static final Paint INVERT_PAINT = createInvertPaint();
|
||||
private static final ColorFilter NO_COLOR_FILTER = new ColorFilter();
|
||||
|
||||
private final Rect mClipBounds = new Rect();
|
||||
|
||||
private View mContractedChild;
|
||||
private View mExpandedChild;
|
||||
|
||||
private NotificationViewWrapper mContractedWrapper;
|
||||
|
||||
private int mSmallHeight;
|
||||
private int mClipTopAmount;
|
||||
private int mActualHeight;
|
||||
@@ -122,6 +122,7 @@ public class NotificationContentView extends FrameLayout {
|
||||
sanitizeContractedLayoutParams(child);
|
||||
addView(child);
|
||||
mContractedChild = child;
|
||||
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
|
||||
selectLayout(false /* animate */, true /* force */);
|
||||
}
|
||||
|
||||
@@ -249,38 +250,10 @@ public class NotificationContentView extends FrameLayout {
|
||||
return mExpandedChild != null;
|
||||
}
|
||||
|
||||
public void setDark(boolean dark, boolean fade) {
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
if (mDark == dark || mContractedChild == null) return;
|
||||
mDark = dark;
|
||||
setImageViewDark(dark, fade, com.android.internal.R.id.right_icon);
|
||||
setImageViewDark(dark, fade, com.android.internal.R.id.icon);
|
||||
}
|
||||
|
||||
private void setImageViewDark(boolean dark, boolean fade, int imageViewId) {
|
||||
// TODO: implement fade
|
||||
final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId);
|
||||
if (v == null) return;
|
||||
final Drawable d = v.getBackground();
|
||||
if (dark) {
|
||||
v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT);
|
||||
if (d != null) {
|
||||
v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter()
|
||||
: NO_COLOR_FILTER);
|
||||
d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha));
|
||||
}
|
||||
} else {
|
||||
v.setLayerType(LAYER_TYPE_NONE, null);
|
||||
if (d != null) {
|
||||
final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag);
|
||||
if (filter != null) {
|
||||
d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter);
|
||||
v.setTag(R.id.doze_saved_filter_tag, null);
|
||||
}
|
||||
v.setImageAlpha(0xff);
|
||||
}
|
||||
}
|
||||
mContractedWrapper.setDark(dark, fade, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -290,16 +263,4 @@ public class NotificationContentView extends FrameLayout {
|
||||
// layout, and saves us some layers.
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Paint createInvertPaint() {
|
||||
final Paint p = new Paint();
|
||||
final float[] invert = {
|
||||
-1f, 0f, 0f, 1f, 1f,
|
||||
0f, -1f, 0f, 1f, 1f,
|
||||
0f, 0f, -1f, 1f, 1f,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
};
|
||||
p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert)));
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.statusbar;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.ViewInvertHelper;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
/**
|
||||
* Wraps a notification containing a custom view.
|
||||
*/
|
||||
public class NotificationCustomViewWrapper extends NotificationViewWrapper {
|
||||
|
||||
private final ViewInvertHelper mInvertHelper;
|
||||
private boolean mDark;
|
||||
|
||||
protected NotificationCustomViewWrapper(View view) {
|
||||
super(view);
|
||||
mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
if (mDark != dark) {
|
||||
mDark = dark;
|
||||
if (fade) {
|
||||
mInvertHelper.fade(dark, delay);
|
||||
} else {
|
||||
mInvertHelper.update(dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Wraps a media notification.
|
||||
*/
|
||||
public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
|
||||
|
||||
private boolean mDark;
|
||||
|
||||
protected NotificationMediaViewWrapper(Context ctx, View view) {
|
||||
super(ctx, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
if (mDark != dark) {
|
||||
mDark = dark;
|
||||
|
||||
// Only update the large icon, because the rest is already inverted.
|
||||
setPictureGrayscale(dark, fade, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.ViewInvertHelper;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
/**
|
||||
* Container view for overflowing notification icons on Keyguard.
|
||||
@@ -28,6 +30,8 @@ import com.android.systemui.R;
|
||||
public class NotificationOverflowContainer extends ActivatableNotificationView {
|
||||
|
||||
private NotificationOverflowIconsView mIconsView;
|
||||
private ViewInvertHelper mViewInvertHelper;
|
||||
private boolean mDark;
|
||||
|
||||
public NotificationOverflowContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -39,6 +43,20 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
|
||||
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
|
||||
mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
|
||||
mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow));
|
||||
mViewInvertHelper = new ViewInvertHelper(findViewById(R.id.content),
|
||||
NotificationPanelView.DOZE_ANIMATION_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
super.setDark(dark, fade, delay);
|
||||
if (mDark == dark) return;
|
||||
mDark = dark;
|
||||
if (fade) {
|
||||
mViewInvertHelper.fade(dark, delay);
|
||||
} else {
|
||||
mViewInvertHelper.update(dark);
|
||||
}
|
||||
}
|
||||
|
||||
public NotificationOverflowIconsView getIconsView() {
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.statusbar;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.ViewInvertHelper;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
|
||||
/**
|
||||
* Wraps a notification view inflated from a template.
|
||||
*/
|
||||
public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
|
||||
|
||||
private final ViewInvertHelper mInvertHelper;
|
||||
private final ImageView mIcon;
|
||||
protected final ImageView mPicture;
|
||||
private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
|
||||
private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
|
||||
0, PorterDuff.Mode.SRC_ATOP);
|
||||
private final int mIconDarkAlpha;
|
||||
private final int mIconBackgroundColor;
|
||||
private final int mIconBackgroundDarkColor;
|
||||
private final Interpolator mLinearOutSlowInInterpolator;
|
||||
|
||||
private boolean mDark;
|
||||
|
||||
protected NotificationTemplateViewWrapper(Context ctx, View view) {
|
||||
super(view);
|
||||
mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
|
||||
mIconBackgroundDarkColor =
|
||||
ctx.getResources().getColor(R.color.doze_small_icon_background_color);
|
||||
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
|
||||
android.R.interpolator.linear_out_slow_in);
|
||||
View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column);
|
||||
mInvertHelper = mainColumn != null
|
||||
? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
|
||||
: null;
|
||||
ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
|
||||
ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon);
|
||||
mIcon = resolveIcon(largeIcon, rightIcon);
|
||||
mPicture = resolvePicture(largeIcon);
|
||||
mIconBackgroundColor = resolveBackgroundColor(mIcon);
|
||||
}
|
||||
|
||||
private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
|
||||
return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
|
||||
: rightIcon != null && rightIcon.getBackground() != null ? rightIcon
|
||||
: null;
|
||||
}
|
||||
|
||||
private ImageView resolvePicture(ImageView largeIcon) {
|
||||
return largeIcon != null && largeIcon.getBackground() == null
|
||||
? largeIcon
|
||||
: null;
|
||||
}
|
||||
|
||||
private int resolveBackgroundColor(ImageView icon) {
|
||||
if (icon != null && icon.getBackground() != null) {
|
||||
ColorFilter filter = icon.getBackground().getColorFilter();
|
||||
if (filter instanceof PorterDuffColorFilter) {
|
||||
return ((PorterDuffColorFilter) filter).getColor();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDark(boolean dark, boolean fade, long delay) {
|
||||
if (mDark != dark) {
|
||||
mDark = dark;
|
||||
if (mInvertHelper != null) {
|
||||
if (fade) {
|
||||
mInvertHelper.fade(dark, delay);
|
||||
} else {
|
||||
mInvertHelper.update(dark);
|
||||
}
|
||||
}
|
||||
if (mIcon != null) {
|
||||
if (fade) {
|
||||
fadeIconColorFilter(mIcon, dark, delay);
|
||||
fadeIconAlpha(mIcon, dark, delay);
|
||||
} else {
|
||||
updateIconColorFilter(mIcon, dark);
|
||||
updateIconAlpha(mIcon, dark);
|
||||
}
|
||||
}
|
||||
setPictureGrayscale(dark, fade, delay);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
|
||||
if (mPicture != null) {
|
||||
if (fade) {
|
||||
fadeGrayscale(mPicture, grayscale, delay);
|
||||
} else {
|
||||
updateGrayscale(mPicture, grayscale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
|
||||
boolean dark, long delay, Animator.AnimatorListener listener) {
|
||||
float startIntensity = dark ? 0f : 1f;
|
||||
float endIntensity = dark ? 1f : 0f;
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
|
||||
animator.addUpdateListener(updateListener);
|
||||
animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
|
||||
animator.setInterpolator(mLinearOutSlowInInterpolator);
|
||||
animator.setStartDelay(delay);
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
|
||||
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateIconColorFilter(target, (Float) animation.getAnimatedValue());
|
||||
}
|
||||
}, dark, delay, null /* listener */);
|
||||
}
|
||||
|
||||
private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
|
||||
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float t = (float) animation.getAnimatedValue();
|
||||
target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
|
||||
}
|
||||
}, dark, delay, null /* listener */);
|
||||
}
|
||||
|
||||
protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
|
||||
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateGrayscaleMatrix((float) animation.getAnimatedValue());
|
||||
target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
|
||||
}
|
||||
}, dark, delay, new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!dark) {
|
||||
target.setColorFilter(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateIconColorFilter(ImageView target, boolean dark) {
|
||||
updateIconColorFilter(target, dark ? 1f : 0f);
|
||||
}
|
||||
|
||||
private void updateIconColorFilter(ImageView target, float intensity) {
|
||||
int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
|
||||
mIconColorFilter.setColor(color);
|
||||
target.getBackground().mutate().setColorFilter(mIconColorFilter);
|
||||
}
|
||||
|
||||
private void updateIconAlpha(ImageView target, boolean dark) {
|
||||
target.setImageAlpha(dark ? mIconDarkAlpha : 255);
|
||||
}
|
||||
|
||||
protected void updateGrayscale(ImageView target, boolean dark) {
|
||||
if (dark) {
|
||||
updateGrayscaleMatrix(1f);
|
||||
target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
|
||||
} else {
|
||||
target.setColorFilter(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGrayscaleMatrix(float intensity) {
|
||||
mGrayscaleColorMatrix.setSaturation(1 - intensity);
|
||||
}
|
||||
|
||||
private static int interpolateColor(int source, int target, float t) {
|
||||
int aSource = Color.alpha(source);
|
||||
int rSource = Color.red(source);
|
||||
int gSource = Color.green(source);
|
||||
int bSource = Color.blue(source);
|
||||
int aTarget = Color.alpha(target);
|
||||
int rTarget = Color.red(target);
|
||||
int gTarget = Color.green(target);
|
||||
int bTarget = Color.blue(target);
|
||||
return Color.argb(
|
||||
(int) (aSource * (1f - t) + aTarget * t),
|
||||
(int) (rSource * (1f - t) + rTarget * t),
|
||||
(int) (gSource * (1f - t) + gTarget * t),
|
||||
(int) (bSource * (1f - t) + bTarget * t));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* Wraps the actual notification content view; used to implement behaviors which are different for
|
||||
* the individual templates and custom views.
|
||||
*/
|
||||
public abstract class NotificationViewWrapper {
|
||||
|
||||
protected final View mView;
|
||||
|
||||
public static NotificationViewWrapper wrap(Context ctx, View v) {
|
||||
|
||||
// TODO: Figure out a better way to find out which template the view is.
|
||||
if (v.findViewById(com.android.internal.R.id.media_actions) != null) {
|
||||
return new NotificationMediaViewWrapper(ctx, v);
|
||||
} else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
|
||||
return new NotificationTemplateViewWrapper(ctx, v);
|
||||
} else {
|
||||
return new NotificationCustomViewWrapper(v);
|
||||
}
|
||||
}
|
||||
|
||||
protected NotificationViewWrapper(View view) {
|
||||
mView = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* In dark mode, we draw as little as possible, assuming a black background.
|
||||
*
|
||||
* @param dark whether we should display ourselves in dark mode
|
||||
* @param fade whether to animate the transition if the mode changes
|
||||
* @param delay if fading, the delay of the animation
|
||||
*/
|
||||
public abstract void setDark(boolean dark, boolean fade, long delay);
|
||||
}
|
||||
@@ -3708,14 +3708,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
|
||||
return;
|
||||
}
|
||||
mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/);
|
||||
if (mDozing) {
|
||||
mStackScroller.setDark(true, false /*animate*/);
|
||||
} else {
|
||||
mStackScroller.setDark(false, false /*animate*/);
|
||||
}
|
||||
boolean animate = !mDozing && mDozeScrimController.isPulsing();
|
||||
mNotificationPanel.setDozing(mDozing, animate);
|
||||
mStackScroller.setDark(mDozing, animate);
|
||||
mScrimController.setDozing(mDozing);
|
||||
mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */);
|
||||
mDozeScrimController.setDozing(mDozing, animate);
|
||||
}
|
||||
|
||||
public void updateStackScrollerState(boolean goingToFullShade) {
|
||||
|
||||
@@ -33,6 +33,7 @@ public class AnimationFilter {
|
||||
boolean animateHideSensitive;
|
||||
boolean hasDelays;
|
||||
boolean hasGoToFullShadeEvent;
|
||||
boolean hasDarkEvent;
|
||||
|
||||
public AnimationFilter animateAlpha() {
|
||||
animateAlpha = true;
|
||||
@@ -98,6 +99,10 @@ public class AnimationFilter {
|
||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
|
||||
hasGoToFullShadeEvent = true;
|
||||
}
|
||||
if (events.get(i).animationType ==
|
||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_DARK) {
|
||||
hasDarkEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,5 +131,6 @@ public class AnimationFilter {
|
||||
animateHideSensitive = false;
|
||||
hasDelays = false;
|
||||
hasGoToFullShadeEvent = false;
|
||||
hasDarkEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2455,7 +2455,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
|
||||
// ANIMATION_TYPE_DARK
|
||||
new AnimationFilter()
|
||||
.animateDark(),
|
||||
.animateDark()
|
||||
.hasDelays(),
|
||||
|
||||
// ANIMATION_TYPE_GO_TO_FULL_SHADE
|
||||
new AnimationFilter()
|
||||
|
||||
@@ -154,7 +154,7 @@ public class StackScrollState {
|
||||
child.setDimmed(state.dimmed, false /* animate */);
|
||||
|
||||
// apply dark
|
||||
child.setDark(state.dark, false /* animate */);
|
||||
child.setDark(state.dark, false /* animate */, 0 /* delay */);
|
||||
|
||||
// apply hiding sensitive
|
||||
child.setHideSensitive(
|
||||
|
||||
@@ -46,6 +46,7 @@ public class StackStateAnimator {
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
|
||||
private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
|
||||
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
|
||||
@@ -161,11 +162,12 @@ public class StackStateAnimator {
|
||||
boolean scaleChanging = child.getScaleX() != viewState.scale;
|
||||
boolean alphaChanging = alpha != child.getAlpha();
|
||||
boolean heightChanging = viewState.height != child.getActualHeight();
|
||||
boolean darkChanging = viewState.dark != child.isDark();
|
||||
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
|
||||
boolean wasAdded = mNewAddChildren.contains(child);
|
||||
boolean hasDelays = mAnimationFilter.hasDelays;
|
||||
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
|
||||
alphaChanging || heightChanging || topInsetChanging;
|
||||
alphaChanging || heightChanging || topInsetChanging || darkChanging;
|
||||
boolean noAnimation = wasAdded;
|
||||
long delay = 0;
|
||||
long duration = mCurrentLength;
|
||||
@@ -242,7 +244,7 @@ public class StackStateAnimator {
|
||||
&& !noAnimation);
|
||||
|
||||
// start dark animation
|
||||
child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation);
|
||||
child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay);
|
||||
|
||||
// apply speed bump state
|
||||
child.setBelowSpeedBump(viewState.belowSpeedBump);
|
||||
@@ -262,6 +264,9 @@ public class StackStateAnimator {
|
||||
|
||||
private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
|
||||
StackScrollState finalState) {
|
||||
if (mAnimationFilter.hasDarkEvent) {
|
||||
return calculateDelayDark(viewState);
|
||||
}
|
||||
if (mAnimationFilter.hasGoToFullShadeEvent) {
|
||||
return calculateDelayGoToFullShade(viewState);
|
||||
}
|
||||
@@ -309,6 +314,10 @@ public class StackStateAnimator {
|
||||
return minDelay;
|
||||
}
|
||||
|
||||
private long calculateDelayDark(StackScrollState.ViewState viewState) {
|
||||
return viewState.notGoneIndex * ANIMATION_DELAY_PER_ELEMENT_DARK;
|
||||
}
|
||||
|
||||
private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
|
||||
float index = viewState.notGoneIndex;
|
||||
index = (float) Math.pow(index, 0.7f);
|
||||
|
||||
Reference in New Issue
Block a user