/* * 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.notification; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.service.notification.StatusBarNotification; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.ViewTransformationHelper; /** * Wraps a notification view inflated from a template. */ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper { private static final int mDarkProgressTint = 0xffffffff; protected ImageView mPicture; private ProgressBar mProgressBar; private TextView mTitle; private TextView mText; private View mActionsContainer; private View mReplyAction; private Rect mTmpRect = new Rect(); private int mContentHeight; private int mMinHeightHint; protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); mTransformationHelper.setCustomTransformation( new ViewTransformationHelper.CustomTransformation() { @Override public boolean transformTo(TransformState ownState, TransformableView notification, final float transformationAmount) { if (!(notification instanceof HybridNotificationView)) { return false; } TransformState otherState = notification.getCurrentState( TRANSFORMING_VIEW_TITLE); final View text = ownState.getTransformedView(); CrossFadeHelper.fadeOut(text, transformationAmount); if (otherState != null) { ownState.transformViewVerticalTo(otherState, this, transformationAmount); otherState.recycle(); } return true; } @Override public boolean customTransformTarget(TransformState ownState, TransformState otherState) { float endY = getTransformationY(ownState, otherState); ownState.setTransformationEndY(endY); return true; } @Override public boolean transformFrom(TransformState ownState, TransformableView notification, float transformationAmount) { if (!(notification instanceof HybridNotificationView)) { return false; } TransformState otherState = notification.getCurrentState( TRANSFORMING_VIEW_TITLE); final View text = ownState.getTransformedView(); CrossFadeHelper.fadeIn(text, transformationAmount); if (otherState != null) { ownState.transformViewVerticalFrom(otherState, this, transformationAmount); otherState.recycle(); } return true; } @Override public boolean initTransformation(TransformState ownState, TransformState otherState) { float startY = getTransformationY(ownState, otherState); ownState.setTransformationStartY(startY); return true; } private float getTransformationY(TransformState ownState, TransformState otherState) { int[] otherStablePosition = otherState.getLaidOutLocationOnScreen(); int[] ownStablePosition = ownState.getLaidOutLocationOnScreen(); return (otherStablePosition[1] + otherState.getTransformedView().getHeight() - ownStablePosition[1]) * 0.33f; } }, TRANSFORMING_VIEW_TEXT); } private void resolveTemplateViews(StatusBarNotification notification) { mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon); if (mPicture != 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; } else { // It's still a viewstub mProgressBar = null; } mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container); mReplyAction = mView.findViewById(com.android.internal.R.id.reply_icon_action); } @Override public boolean disallowSingleClick(float x, float y) { if (mReplyAction != null && mReplyAction.getVisibility() == View.VISIBLE) { if (isOnView(mReplyAction, x, y) || isOnView(mPicture, x, y)) { return true; } } return super.disallowSingleClick(x, y); } private boolean isOnView(View view, float x, float y) { View searchView = (View) view.getParent(); while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) { searchView.getHitRect(mTmpRect); x -= mTmpRect.left; y -= mTmpRect.top; searchView = (View) searchView.getParent(); } view.getHitRect(mTmpRect); return mTmpRect.contains((int) x,(int) y); } @Override public void onContentUpdated(ExpandableNotificationRow row) { // Reinspect the notification. Before the super call, because the super call also updates // the transformation types and we need to have our values set by then. resolveTemplateViews(row.getStatusBarNotification()); super.onContentUpdated(row); } @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 protected void updateTransformedTypes() { // This also clears the existing types super.updateTransformedTypes(); if (mTitle != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle); } if (mText != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText); } if (mPicture != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture); } if (mProgressBar != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar); } } @Override public void setDark(boolean dark, boolean fade, long delay) { if (dark == mDark && mDarkInitialized) { return; } super.setDark(dark, fade, delay); setPictureDark(dark, fade, delay); setProgressBarDark(dark, fade, delay); } private void setProgressBarDark(boolean dark, boolean fade, long delay) { if (mProgressBar != null) { if (fade) { fadeProgressDark(mProgressBar, dark, delay); } else { updateProgressDark(mProgressBar, dark); } } } private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) { getDozer().startIntensityAnimation(animation -> { float t = (float) animation.getAnimatedValue(); updateProgressDark(target, t); }, dark, delay, null /* listener */); } private void updateProgressDark(ProgressBar target, float intensity) { int color = interpolateColor(mColor, mDarkProgressTint, intensity); target.getIndeterminateDrawable().mutate().setTint(color); target.getProgressDrawable().mutate().setTint(color); } private void updateProgressDark(ProgressBar target, boolean dark) { updateProgressDark(target, dark ? 1f : 0f); } private void setPictureDark(boolean dark, boolean fade, long delay) { if (mPicture != null) { getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */); } } 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)); } @Override public void setContentHeight(int contentHeight, int minHeightHint) { super.setContentHeight(contentHeight, minHeightHint); mContentHeight = contentHeight; mMinHeightHint = minHeightHint; updateActionOffset(); } private void updateActionOffset() { if (mActionsContainer != null) { // We should never push the actions higher than they are in the headsup view. int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint); mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()); } } }