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 static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
     20 
     21 import android.app.Notification;
     22 import android.content.Context;
     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.R;
     35 import com.android.systemui.statusbar.ExpandableNotificationRow;
     36 import com.android.systemui.statusbar.TransformableView;
     37 import com.android.systemui.statusbar.ViewTransformationHelper;
     38 
     39 import java.util.Stack;
     40 
     41 /**
     42  * Wraps a notification header view.
     43  */
     44 public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
     45 
     46     private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
     47             = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
     48 
     49     protected final ViewTransformationHelper mTransformationHelper;
     50     private final int mTranslationForHeader;
     51 
     52     protected int mColor;
     53     private ImageView mIcon;
     54 
     55     private NotificationExpandButton mExpandButton;
     56     private NotificationHeaderView mNotificationHeader;
     57     private TextView mHeaderText;
     58     private ImageView mWorkProfileImage;
     59     private boolean mIsLowPriority;
     60     private boolean mTransformLowPriorityTitle;
     61     private boolean mShowExpandButtonAtEnd;
     62     protected float mHeaderTranslation;
     63 
     64     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
     65         super(ctx, view, row);
     66         mShowExpandButtonAtEnd = ctx.getResources().getBoolean(
     67                 R.bool.config_showNotificationExpandButtonAtEnd);
     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         addAppOpsOnClickListener(row);
     97         mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
     98                 com.android.internal.R.dimen.notification_content_margin)
     99                 - ctx.getResources().getDimensionPixelSize(
    100                         com.android.internal.R.dimen.notification_content_margin_top);
    101     }
    102 
    103     protected void resolveHeaderViews() {
    104         mIcon = mView.findViewById(com.android.internal.R.id.icon);
    105         mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
    106         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
    107         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
    108         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
    109         mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
    110         mColor = mNotificationHeader.getOriginalIconColor();
    111     }
    112 
    113     private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
    114         mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
    115     }
    116 
    117     @Override
    118     public void onContentUpdated(ExpandableNotificationRow row) {
    119         super.onContentUpdated(row);
    120         mIsLowPriority = row.isLowPriority();
    121         mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
    122         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
    123 
    124         // Reinspect the notification.
    125         resolveHeaderViews();
    126         updateTransformedTypes();
    127         addRemainingTransformTypes();
    128         updateCropToPaddingForImageViews();
    129         Notification notification = row.getStatusBarNotification().getNotification();
    130         mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
    131         // The work profile image is always the same lets just set the icon tag for it not to
    132         // animate
    133         mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
    134 
    135         // We need to reset all views that are no longer transforming in case a view was previously
    136         // transformed, but now we decided to transform its container instead.
    137         ArraySet<View> currentViews = mTransformationHelper.getAllTransformingViews();
    138         for (int i = 0; i < previousViews.size(); i++) {
    139             View view = previousViews.valueAt(i);
    140             if (!currentViews.contains(view)) {
    141                 mTransformationHelper.resetTransformedView(view);
    142             }
    143         }
    144     }
    145 
    146     /**
    147      * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
    148      * child is faded automatically and doesn't have to be manually added.
    149      * The keys used for the views are the ids.
    150      */
    151     private void addRemainingTransformTypes() {
    152         mTransformationHelper.addRemainingTransformTypes(mView);
    153     }
    154 
    155     /**
    156      * Since we are deactivating the clipping when transforming the ImageViews don't get clipped
    157      * anymore during these transitions. We can avoid that by using
    158      * {@link ImageView#setCropToPadding(boolean)} on all ImageViews.
    159      */
    160     private void updateCropToPaddingForImageViews() {
    161         Stack<View> stack = new Stack<>();
    162         stack.push(mView);
    163         while (!stack.isEmpty()) {
    164             View child = stack.pop();
    165             if (child instanceof ImageView) {
    166                 ((ImageView) child).setCropToPadding(true);
    167             } else if (child instanceof ViewGroup){
    168                 ViewGroup group = (ViewGroup) child;
    169                 for (int i = 0; i < group.getChildCount(); i++) {
    170                     stack.push(group.getChildAt(i));
    171                 }
    172             }
    173         }
    174     }
    175 
    176     protected void updateTransformedTypes() {
    177         mTransformationHelper.reset();
    178         mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
    179         if (mIsLowPriority) {
    180             mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
    181                     mHeaderText);
    182         }
    183     }
    184 
    185     @Override
    186     public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
    187         mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
    188         mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
    189     }
    190 
    191     @Override
    192     public void setHeaderVisibleAmount(float headerVisibleAmount) {
    193         super.setHeaderVisibleAmount(headerVisibleAmount);
    194         mNotificationHeader.setAlpha(headerVisibleAmount);
    195         mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
    196         mView.setTranslationY(mHeaderTranslation);
    197     }
    198 
    199     @Override
    200     public int getHeaderTranslation() {
    201         return (int) mHeaderTranslation;
    202     }
    203 
    204     @Override
    205     public NotificationHeaderView getNotificationHeader() {
    206         return mNotificationHeader;
    207     }
    208 
    209     @Override
    210     public TransformState getCurrentState(int fadingView) {
    211         return mTransformationHelper.getCurrentState(fadingView);
    212     }
    213 
    214     @Override
    215     public void transformTo(TransformableView notification, Runnable endRunnable) {
    216         mTransformationHelper.transformTo(notification, endRunnable);
    217     }
    218 
    219     @Override
    220     public void transformTo(TransformableView notification, float transformationAmount) {
    221         mTransformationHelper.transformTo(notification, transformationAmount);
    222     }
    223 
    224     @Override
    225     public void transformFrom(TransformableView notification) {
    226         mTransformationHelper.transformFrom(notification);
    227     }
    228 
    229     @Override
    230     public void transformFrom(TransformableView notification, float transformationAmount) {
    231         mTransformationHelper.transformFrom(notification, transformationAmount);
    232     }
    233 
    234     @Override
    235     public void setIsChildInGroup(boolean isChildInGroup) {
    236         super.setIsChildInGroup(isChildInGroup);
    237         mTransformLowPriorityTitle = !isChildInGroup;
    238     }
    239 
    240     @Override
    241     public void setVisible(boolean visible) {
    242         super.setVisible(visible);
    243         mTransformationHelper.setVisible(visible);
    244     }
    245 }
    246