1 /* 2 * Copyright (C) 2014 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; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.graphics.Outline; 22 import android.graphics.Rect; 23 import android.graphics.RectF; 24 import android.util.AttributeSet; 25 import android.view.View; 26 import android.view.ViewOutlineProvider; 27 import com.android.systemui.R; 28 29 /** 30 * Like {@link ExpandableView}, but setting an outline for the height and clipping. 31 */ 32 public abstract class ExpandableOutlineView extends ExpandableView { 33 34 private final Rect mOutlineRect = new Rect(); 35 private boolean mCustomOutline; 36 private float mOutlineAlpha = -1f; 37 private float mOutlineRadius; 38 39 /** 40 * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when 41 * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. 42 */ 43 protected boolean mShouldTranslateContents; 44 45 private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { 46 @Override 47 public void getOutline(View view, Outline outline) { 48 int translation = mShouldTranslateContents ? (int) getTranslation() : 0; 49 if (!mCustomOutline) { 50 outline.setRoundRect(translation, 51 mClipTopAmount, 52 getWidth() + translation, 53 Math.max(getActualHeight() - mClipBottomAmount, mClipTopAmount), 54 mOutlineRadius); 55 } else { 56 outline.setRoundRect(mOutlineRect, mOutlineRadius); 57 } 58 outline.setAlpha(mOutlineAlpha); 59 } 60 }; 61 62 public ExpandableOutlineView(Context context, AttributeSet attrs) { 63 super(context, attrs); 64 setOutlineProvider(mProvider); 65 initDimens(); 66 } 67 68 private void initDimens() { 69 Resources res = getResources(); 70 mShouldTranslateContents = 71 res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe); 72 mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius); 73 setClipToOutline(res.getBoolean(R.bool.config_clipNotificationsToOutline)); 74 } 75 76 public void onDensityOrFontScaleChanged() { 77 initDimens(); 78 invalidateOutline(); 79 } 80 81 @Override 82 public void setActualHeight(int actualHeight, boolean notifyListeners) { 83 super.setActualHeight(actualHeight, notifyListeners); 84 invalidateOutline(); 85 } 86 87 @Override 88 public void setClipTopAmount(int clipTopAmount) { 89 super.setClipTopAmount(clipTopAmount); 90 invalidateOutline(); 91 } 92 93 @Override 94 public void setClipBottomAmount(int clipBottomAmount) { 95 super.setClipBottomAmount(clipBottomAmount); 96 invalidateOutline(); 97 } 98 99 protected void setOutlineAlpha(float alpha) { 100 if (alpha != mOutlineAlpha) { 101 mOutlineAlpha = alpha; 102 invalidateOutline(); 103 } 104 } 105 106 @Override 107 public float getOutlineAlpha() { 108 return mOutlineAlpha; 109 } 110 111 protected void setOutlineRect(RectF rect) { 112 if (rect != null) { 113 setOutlineRect(rect.left, rect.top, rect.right, rect.bottom); 114 } else { 115 mCustomOutline = false; 116 setClipToOutline(false); 117 invalidateOutline(); 118 } 119 } 120 121 @Override 122 public int getOutlineTranslation() { 123 return mCustomOutline ? mOutlineRect.left : (int) getTranslation(); 124 } 125 126 public void updateOutline() { 127 if (mCustomOutline) { 128 return; 129 } 130 boolean hasOutline = needsOutline(); 131 setOutlineProvider(hasOutline ? mProvider : null); 132 } 133 134 /** 135 * @return Whether the view currently needs an outline. This is usually {@code false} in case 136 * it doesn't have a background. 137 */ 138 protected boolean needsOutline() { 139 if (isChildInGroup()) { 140 return isGroupExpanded() && !isGroupExpansionChanging(); 141 } else if (isSummaryWithChildren()) { 142 return !isGroupExpanded() || isGroupExpansionChanging(); 143 } 144 return true; 145 } 146 147 public boolean isOutlineShowing() { 148 ViewOutlineProvider op = getOutlineProvider(); 149 return op != null; 150 } 151 152 protected void setOutlineRect(float left, float top, float right, float bottom) { 153 mCustomOutline = true; 154 setClipToOutline(true); 155 156 mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom); 157 158 // Outlines need to be at least 1 dp 159 mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom); 160 mOutlineRect.right = (int) Math.max(left, mOutlineRect.right); 161 162 invalidateOutline(); 163 } 164 165 } 166