| /* |
| * Copyright (C) 2015 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.row.wrapper; |
| |
| import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; |
| |
| import android.app.Notification; |
| import android.content.Context; |
| import android.util.ArraySet; |
| import android.util.Pair; |
| import android.view.NotificationHeaderView; |
| import android.view.NotificationTopLineView; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.animation.Interpolator; |
| import android.view.animation.PathInterpolator; |
| import android.widget.ImageButton; |
| import android.widget.ImageView; |
| import android.widget.TextView; |
| |
| import com.android.internal.widget.CachingIconView; |
| import com.android.internal.widget.NotificationExpandButton; |
| import com.android.systemui.animation.Interpolators; |
| import com.android.systemui.statusbar.TransformableView; |
| import com.android.systemui.statusbar.ViewTransformationHelper; |
| import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation; |
| import com.android.systemui.statusbar.notification.ImageTransformState; |
| import com.android.systemui.statusbar.notification.TransformState; |
| import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; |
| |
| import java.util.Stack; |
| |
| /** |
| * Wraps a notification view which may or may not include a header. |
| */ |
| public class NotificationHeaderViewWrapper extends NotificationViewWrapper { |
| |
| private static final Interpolator LOW_PRIORITY_HEADER_CLOSE |
| = new PathInterpolator(0.4f, 0f, 0.7f, 1f); |
| |
| protected final ViewTransformationHelper mTransformationHelper; |
| |
| private CachingIconView mIcon; |
| private NotificationExpandButton mExpandButton; |
| private View mAltExpandTarget; |
| private View mIconContainer; |
| protected NotificationHeaderView mNotificationHeader; |
| protected NotificationTopLineView mNotificationTopLine; |
| private TextView mHeaderText; |
| private TextView mAppNameText; |
| private ImageView mWorkProfileImage; |
| private View mAudiblyAlertedIcon; |
| private View mFeedbackIcon; |
| |
| private boolean mIsLowPriority; |
| private boolean mTransformLowPriorityTitle; |
| |
| protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { |
| super(ctx, view, row); |
| mTransformationHelper = new ViewTransformationHelper(); |
| |
| // we want to avoid that the header clashes with the other text when transforming |
| // low-priority |
| mTransformationHelper.setCustomTransformation( |
| new CustomInterpolatorTransformation(TRANSFORMING_VIEW_TITLE) { |
| |
| @Override |
| public Interpolator getCustomInterpolator(int interpolationType, |
| boolean isFrom) { |
| boolean isLowPriority = mView instanceof NotificationHeaderView; |
| if (interpolationType == TRANSFORM_Y) { |
| if (isLowPriority && !isFrom |
| || !isLowPriority && isFrom) { |
| return Interpolators.LINEAR_OUT_SLOW_IN; |
| } else { |
| return LOW_PRIORITY_HEADER_CLOSE; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| protected boolean hasCustomTransformation() { |
| return mIsLowPriority && mTransformLowPriorityTitle; |
| } |
| }, TRANSFORMING_VIEW_TITLE); |
| resolveHeaderViews(); |
| addFeedbackOnClickListener(row); |
| } |
| |
| protected void resolveHeaderViews() { |
| mIcon = mView.findViewById(com.android.internal.R.id.icon); |
| mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); |
| mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); |
| mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); |
| mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); |
| mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container); |
| mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); |
| mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); |
| mNotificationTopLine = mView.findViewById(com.android.internal.R.id.notification_top_line); |
| mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon); |
| mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback); |
| } |
| |
| private void addFeedbackOnClickListener(ExpandableNotificationRow row) { |
| View.OnClickListener listener = row.getFeedbackOnClickListener(); |
| if (mNotificationTopLine != null) { |
| mNotificationTopLine.setFeedbackOnClickListener(listener); |
| } |
| if (mFeedbackIcon != null) { |
| mFeedbackIcon.setOnClickListener(listener); |
| } |
| } |
| |
| /** Shows or hides feedback indicator */ |
| @Override |
| public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) { |
| if (mFeedbackIcon != null) { |
| mFeedbackIcon.setVisibility(show ? View.VISIBLE : View.GONE); |
| if (show) { |
| if (mFeedbackIcon instanceof ImageButton) { |
| ((ImageButton) mFeedbackIcon).setImageResource(resIds.first); |
| } |
| mFeedbackIcon.setContentDescription(mView.getContext().getString(resIds.second)); |
| } |
| } |
| } |
| |
| @Override |
| public void onContentUpdated(ExpandableNotificationRow row) { |
| super.onContentUpdated(row); |
| mIsLowPriority = row.getEntry().isAmbient(); |
| mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren(); |
| ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews(); |
| |
| // Reinspect the notification. |
| resolveHeaderViews(); |
| updateTransformedTypes(); |
| addRemainingTransformTypes(); |
| updateCropToPaddingForImageViews(); |
| Notification notification = row.getEntry().getSbn().getNotification(); |
| mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); |
| |
| // We need to reset all views that are no longer transforming in case a view was previously |
| // transformed, but now we decided to transform its container instead. |
| ArraySet<View> currentViews = mTransformationHelper.getAllTransformingViews(); |
| for (int i = 0; i < previousViews.size(); i++) { |
| View view = previousViews.valueAt(i); |
| if (!currentViews.contains(view)) { |
| mTransformationHelper.resetTransformedView(view); |
| } |
| } |
| } |
| |
| /** |
| * 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() { |
| mTransformationHelper.addRemainingTransformTypes(mView); |
| } |
| |
| /** |
| * Since we are deactivating the clipping when transforming the ImageViews don't get clipped |
| * anymore during these transitions. We can avoid that by using |
| * {@link ImageView#setCropToPadding(boolean)} on all ImageViews. |
| */ |
| private void updateCropToPaddingForImageViews() { |
| Stack<View> stack = new Stack<>(); |
| stack.push(mView); |
| while (!stack.isEmpty()) { |
| View child = stack.pop(); |
| if (child instanceof ImageView |
| // Skip the importance ring for conversations, disabled cropping is needed for |
| // its animation |
| && child.getId() != com.android.internal.R.id.conversation_icon_badge_ring) { |
| ((ImageView) child).setCropToPadding(true); |
| } else if (child instanceof ViewGroup){ |
| ViewGroup group = (ViewGroup) child; |
| for (int i = 0; i < group.getChildCount(); i++) { |
| stack.push(group.getChildAt(i)); |
| } |
| } |
| } |
| } |
| |
| protected void updateTransformedTypes() { |
| mTransformationHelper.reset(); |
| mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon); |
| mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER, |
| mExpandButton); |
| if (mIsLowPriority && mHeaderText != null) { |
| mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, |
| mHeaderText); |
| } |
| addViewsTransformingToSimilar(mWorkProfileImage, mAudiblyAlertedIcon, mFeedbackIcon); |
| } |
| |
| @Override |
| public void updateExpandability(boolean expandable, View.OnClickListener onClickListener, |
| boolean requestLayout) { |
| mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE); |
| mExpandButton.setOnClickListener(expandable ? onClickListener : null); |
| if (mAltExpandTarget != null) { |
| mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null); |
| } |
| if (mIconContainer != null) { |
| mIconContainer.setOnClickListener(expandable ? onClickListener : null); |
| } |
| if (mNotificationHeader != null) { |
| mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); |
| } |
| // Unfortunately, the NotificationContentView has to layout its children in order to |
| // determine their heights, and that affects the button visibility. If that happens |
| // (thankfully it is rare) then we need to request layout of the expand button's parent |
| // in order to ensure it gets laid out correctly. |
| if (requestLayout) { |
| mExpandButton.getParent().requestLayout(); |
| } |
| } |
| |
| @Override |
| public void setExpanded(boolean expanded) { |
| mExpandButton.setExpanded(expanded); |
| } |
| |
| @Override |
| public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { |
| if (mAudiblyAlertedIcon != null) { |
| mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE); |
| } |
| } |
| |
| @Override |
| public NotificationHeaderView getNotificationHeader() { |
| return mNotificationHeader; |
| } |
| |
| @Override |
| public View getExpandButton() { |
| return mExpandButton; |
| } |
| |
| @Override |
| public CachingIconView getIcon() { |
| return mIcon; |
| } |
| |
| @Override |
| public int getOriginalIconColor() { |
| return mIcon.getOriginalIconColor(); |
| } |
| |
| @Override |
| public View getShelfTransformationTarget() { |
| return mIcon; |
| } |
| |
| @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 transformTo(TransformableView notification, float transformationAmount) { |
| mTransformationHelper.transformTo(notification, transformationAmount); |
| } |
| |
| @Override |
| public void transformFrom(TransformableView notification) { |
| mTransformationHelper.transformFrom(notification); |
| } |
| |
| @Override |
| public void transformFrom(TransformableView notification, float transformationAmount) { |
| mTransformationHelper.transformFrom(notification, transformationAmount); |
| } |
| |
| @Override |
| public void setIsChildInGroup(boolean isChildInGroup) { |
| super.setIsChildInGroup(isChildInGroup); |
| mTransformLowPriorityTitle = !isChildInGroup; |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| super.setVisible(visible); |
| mTransformationHelper.setVisible(visible); |
| } |
| |
| protected void addTransformedViews(View... views) { |
| for (View view : views) { |
| if (view != null) { |
| mTransformationHelper.addTransformedView(view); |
| } |
| } |
| } |
| |
| protected void addViewsTransformingToSimilar(View... views) { |
| for (View view : views) { |
| if (view != null) { |
| mTransformationHelper.addViewTransformingToSimilar(view); |
| } |
| } |
| } |
| } |