Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.statusbar.notification;
     18 
     19 import android.app.Notification;
     20 import android.content.Context;
     21 import android.graphics.ColorFilter;
     22 import android.graphics.PorterDuffColorFilter;
     23 import android.util.ArraySet;
     24 import android.view.NotificationHeaderView;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.view.animation.Interpolator;
     28 import android.view.animation.PathInterpolator;
     29 import android.widget.ImageView;
     30 import android.widget.TextView;
     31 
     32 import com.android.internal.widget.NotificationExpandButton;
     33 import com.android.systemui.Interpolators;
     34 import com.android.systemui.ViewInvertHelper;
     35 import com.android.systemui.statusbar.ExpandableNotificationRow;
     36 import com.android.systemui.statusbar.TransformableView;
     37 import com.android.systemui.statusbar.ViewTransformationHelper;
     38 import com.android.systemui.statusbar.phone.NotificationPanelView;
     39 
     40 import java.util.Stack;
     41 
     42 import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
     43 
     44 /**
     45  * Wraps a notification header view.
     46  */
     47 public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
     48 
     49     private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
     50             = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
     51 
     52     protected final ViewInvertHelper mInvertHelper;
     53     protected final ViewTransformationHelper mTransformationHelper;
     54 
     55     protected int mColor;
     56     private ImageView mIcon;
     57 
     58     private NotificationExpandButton mExpandButton;
     59     private NotificationHeaderView mNotificationHeader;
     60     private TextView mHeaderText;
     61     private ImageView mWorkProfileImage;
     62     private boolean mIsLowPriority;
     63     private boolean mTransformLowPriorityTitle;
     64 
     65     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
     66         super(ctx, view, row);
     67         mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
     68         mTransformationHelper = new ViewTransformationHelper();
     69 
     70         // we want to avoid that the header clashes with the other text when transforming
     71         // low-priority
     72         mTransformationHelper.setCustomTransformation(
     73                 new CustomInterpolatorTransformation(TRANSFORMING_VIEW_TITLE) {
     74 
     75                     @Override
     76                     public Interpolator getCustomInterpolator(int interpolationType,
     77                             boolean isFrom) {
     78                         boolean isLowPriority = mView instanceof NotificationHeaderView;
     79                         if (interpolationType == TRANSFORM_Y) {
     80                             if (isLowPriority && !isFrom
     81                                     || !isLowPriority && isFrom) {
     82                                 return Interpolators.LINEAR_OUT_SLOW_IN;
     83                             } else {
     84                                 return LOW_PRIORITY_HEADER_CLOSE;
     85                             }
     86                         }
     87                         return null;
     88                     }
     89 
     90                     @Override
     91                     protected boolean hasCustomTransformation() {
     92                         return mIsLowPriority && mTransformLowPriorityTitle;
     93                     }
     94                 }, TRANSFORMING_VIEW_TITLE);
     95         resolveHeaderViews();
     96         updateInvertHelper();
     97     }
     98 
     99     @Override
    100     protected NotificationDozeHelper createDozer(Context ctx) {
    101         return new NotificationIconDozeHelper(ctx);
    102     }
    103 
    104     @Override
    105     protected NotificationIconDozeHelper getDozer() {
    106         return (NotificationIconDozeHelper) super.getDozer();
    107     }
    108 
    109     protected void resolveHeaderViews() {
    110         mIcon = mView.findViewById(com.android.internal.R.id.icon);
    111         mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
    112         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
    113         mExpandButton.setLabeledBy(mRow);
    114         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
    115         mColor = resolveColor(mExpandButton);
    116         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
    117         getDozer().setColor(mColor);
    118     }
    119 
    120     private int resolveColor(ImageView icon) {
    121         if (icon != null && icon.getDrawable() != null) {
    122             ColorFilter filter = icon.getDrawable().getColorFilter();
    123             if (filter instanceof PorterDuffColorFilter) {
    124                 return ((PorterDuffColorFilter) filter).getColor();
    125             }
    126         }
    127         return 0;
    128     }
    129 
    130     @Override
    131     public void onContentUpdated(ExpandableNotificationRow row) {
    132         super.onContentUpdated(row);
    133         mIsLowPriority = row.isLowPriority();
    134         mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
    135         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
    136 
    137         // Reinspect the notification.
    138         resolveHeaderViews();
    139         updateInvertHelper();
    140         updateTransformedTypes();
    141         addRemainingTransformTypes();
    142         updateCropToPaddingForImageViews();
    143         Notification notification = row.getStatusBarNotification().getNotification();
    144         mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
    145         // The work profile image is always the same lets just set the icon tag for it not to
    146         // animate
    147         mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
    148 
    149         // We need to reset all views that are no longer transforming in case a view was previously
    150         // transformed, but now we decided to transform its container instead.
    151         ArraySet<View> currentViews = mTransformationHelper.getAllTransformingViews();
    152         for (int i = 0; i < previousViews.size(); i++) {
    153             View view = previousViews.valueAt(i);
    154             if (!currentViews.contains(view)) {
    155                 mTransformationHelper.resetTransformedView(view);
    156             }
    157         }
    158     }
    159 
    160     /**
    161      * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
    162      * child is faded automatically and doesn't have to be manually added.
    163      * The keys used for the views are the ids.
    164      */
    165     private void addRemainingTransformTypes() {
    166         mTransformationHelper.addRemainingTransformTypes(mView);
    167     }
    168 
    169     /**
    170      * Since we are deactivating the clipping when transforming the ImageViews don't get clipped
    171      * anymore during these transitions. We can avoid that by using
    172      * {@link ImageView#setCropToPadding(boolean)} on all ImageViews.
    173      */
    174     private void updateCropToPaddingForImageViews() {
    175         Stack<View> stack = new Stack<>();
    176         stack.push(mView);
    177         while (!stack.isEmpty()) {
    178             View child = stack.pop();
    179             if (child instanceof ImageView) {
    180                 ((ImageView) child).setCropToPadding(true);
    181             } else if (child instanceof ViewGroup){
    182                 ViewGroup group = (ViewGroup) child;
    183                 for (int i = 0; i < group.getChildCount(); i++) {
    184                     stack.push(group.getChildAt(i));
    185                 }
    186             }
    187         }
    188     }
    189 
    190     protected void updateInvertHelper() {
    191         mInvertHelper.clearTargets();
    192         for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
    193             View child = mNotificationHeader.getChildAt(i);
    194             if (child != mIcon) {
    195                 mInvertHelper.addTarget(child);
    196             }
    197         }
    198     }
    199 
    200     protected void updateTransformedTypes() {
    201         mTransformationHelper.reset();
    202         mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
    203         if (mIsLowPriority) {
    204             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
    205                     mHeaderText);
    206         }
    207     }
    208 
    209     @Override
    210     public void setDark(boolean dark, boolean fade, long delay) {
    211         if (dark == mDark && mDarkInitialized) {
    212             return;
    213         }
    214         super.setDark(dark, fade, delay);
    215         if (fade) {
    216             mInvertHelper.fade(dark, delay);
    217         } else {
    218             mInvertHelper.update(dark);
    219         }
    220         if (mIcon != null && !mRow.isChildInGroup()) {
    221             // We don't update the color for children views / their icon is invisible anyway.
    222             // It also may lead to bugs where the icon isn't correctly greyed out.
    223             boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
    224                     != NotificationHeaderView.NO_COLOR;
    225 
    226             getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter);
    227         }
    228     }
    229 
    230     @Override
    231     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
    232         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
    233         mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
    234     }
    235 
    236     @Override
    237     public NotificationHeaderView getNotificationHeader() {
    238         return mNotificationHeader;
    239     }
    240 
    241     @Override
    242     public TransformState getCurrentState(int fadingView) {
    243         return mTransformationHelper.getCurrentState(fadingView);
    244     }
    245 
    246     @Override
    247     public void transformTo(TransformableView notification, Runnable endRunnable) {
    248         mTransformationHelper.transformTo(notification, endRunnable);
    249     }
    250 
    251     @Override
    252     public void transformTo(TransformableView notification, float transformationAmount) {
    253         mTransformationHelper.transformTo(notification, transformationAmount);
    254     }
    255 
    256     @Override
    257     public void transformFrom(TransformableView notification) {
    258         mTransformationHelper.transformFrom(notification);
    259     }
    260 
    261     @Override
    262     public void transformFrom(TransformableView notification, float transformationAmount) {
    263         mTransformationHelper.transformFrom(notification, transformationAmount);
    264     }
    265 
    266     @Override
    267     public void setIsChildInGroup(boolean isChildInGroup) {
    268         super.setIsChildInGroup(isChildInGroup);
    269         mTransformLowPriorityTitle = !isChildInGroup;
    270     }
    271 
    272     @Override
    273     public void setVisible(boolean visible) {
    274         super.setVisible(visible);
    275         mTransformationHelper.setVisible(visible);
    276     }
    277 }
    278