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 18 package com.android.internal.widget; 19 20 import android.graphics.Canvas; 21 import android.graphics.PixelFormat; 22 import android.graphics.drawable.Drawable; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 /** 27 * Helper class for drawing a fallback background in framework decor layouts. 28 * Useful for when an app has not set a window background but we're asked to draw 29 * an uncovered area. 30 */ 31 public class BackgroundFallback { 32 private Drawable mBackgroundFallback; 33 setDrawable(Drawable d)34 public void setDrawable(Drawable d) { 35 mBackgroundFallback = d; 36 } 37 hasFallback()38 public boolean hasFallback() { 39 return mBackgroundFallback != null; 40 } 41 42 /** 43 * Draws the fallback background. 44 * 45 * @param boundsView The view determining with which bounds the background should be drawn. 46 * @param root The view group containing the content. 47 * @param c The canvas to draw the background onto. 48 * @param content The view where the actual app content is contained in. 49 * @param coveringView1 A potentially opaque view drawn atop the content 50 * @param coveringView2 A potentially opaque view drawn atop the content 51 */ draw(ViewGroup boundsView, ViewGroup root, Canvas c, View content, View coveringView1, View coveringView2)52 public void draw(ViewGroup boundsView, ViewGroup root, Canvas c, View content, 53 View coveringView1, View coveringView2) { 54 if (!hasFallback()) { 55 return; 56 } 57 58 // Draw the fallback in the padding. 59 final int width = boundsView.getWidth(); 60 final int height = boundsView.getHeight(); 61 62 final int rootOffsetX = root.getLeft(); 63 final int rootOffsetY = root.getTop(); 64 65 int left = width; 66 int top = height; 67 int right = 0; 68 int bottom = 0; 69 70 final int childCount = root.getChildCount(); 71 for (int i = 0; i < childCount; i++) { 72 final View child = root.getChildAt(i); 73 final Drawable childBg = child.getBackground(); 74 if (child == content) { 75 // We always count the content view container unless it has no background 76 // and no children. 77 if (childBg == null && child instanceof ViewGroup && 78 ((ViewGroup) child).getChildCount() == 0) { 79 continue; 80 } 81 } else if (child.getVisibility() != View.VISIBLE || !isOpaque(childBg)) { 82 // Potentially translucent or invisible children don't count, and we assume 83 // the content view will cover the whole area if we're in a background 84 // fallback situation. 85 continue; 86 } 87 left = Math.min(left, rootOffsetX + child.getLeft()); 88 top = Math.min(top, rootOffsetY + child.getTop()); 89 right = Math.max(right, rootOffsetX + child.getRight()); 90 bottom = Math.max(bottom, rootOffsetY + child.getBottom()); 91 } 92 93 // If one of the bar backgrounds is a solid color and covers the entire padding on a side 94 // we can drop that padding. 95 boolean eachBarCoversTopInY = true; 96 for (int i = 0; i < 2; i++) { 97 View v = (i == 0) ? coveringView1 : coveringView2; 98 if (v == null || v.getVisibility() != View.VISIBLE 99 || v.getAlpha() != 1f || !isOpaque(v.getBackground())) { 100 eachBarCoversTopInY = false; 101 continue; 102 } 103 104 // Bar covers entire left padding 105 if (v.getTop() <= 0 && v.getBottom() >= height 106 && v.getLeft() <= 0 && v.getRight() >= left) { 107 left = 0; 108 } 109 // Bar covers entire right padding 110 if (v.getTop() <= 0 && v.getBottom() >= height 111 && v.getLeft() <= right && v.getRight() >= width) { 112 right = width; 113 } 114 // Bar covers entire top padding 115 if (v.getTop() <= 0 && v.getBottom() >= top 116 && v.getLeft() <= 0 && v.getRight() >= width) { 117 top = 0; 118 } 119 // Bar covers entire bottom padding 120 if (v.getTop() <= bottom && v.getBottom() >= height 121 && v.getLeft() <= 0 && v.getRight() >= width) { 122 bottom = height; 123 } 124 125 eachBarCoversTopInY &= v.getTop() <= 0 && v.getBottom() >= top; 126 } 127 128 // Special case: Sometimes, both covering views together may cover the top inset, but 129 // neither does on its own. 130 if (eachBarCoversTopInY && (viewsCoverEntireWidth(coveringView1, coveringView2, width) 131 || viewsCoverEntireWidth(coveringView2, coveringView1, width))) { 132 top = 0; 133 } 134 135 if (left >= right || top >= bottom) { 136 // No valid area to draw in. 137 return; 138 } 139 140 if (top > 0) { 141 mBackgroundFallback.setBounds(0, 0, width, top); 142 mBackgroundFallback.draw(c); 143 } 144 if (left > 0) { 145 mBackgroundFallback.setBounds(0, top, left, height); 146 mBackgroundFallback.draw(c); 147 } 148 if (right < width) { 149 mBackgroundFallback.setBounds(right, top, width, height); 150 mBackgroundFallback.draw(c); 151 } 152 if (bottom < height) { 153 mBackgroundFallback.setBounds(left, bottom, right, height); 154 mBackgroundFallback.draw(c); 155 } 156 } 157 isOpaque(Drawable childBg)158 private boolean isOpaque(Drawable childBg) { 159 return childBg != null && childBg.getOpacity() == PixelFormat.OPAQUE; 160 } 161 162 /** 163 * Returns true if {@code view1} starts before or on {@code 0} and extends at least 164 * up to {@code view2}, and that view extends at least to {@code width}. 165 * 166 * @param view1 the first view to check if it covers the width 167 * @param view2 the second view to check if it covers the width 168 * @param width the width to check for 169 * @return returns true if both views together cover the entire width (and view1 is to the left 170 * of view2) 171 */ viewsCoverEntireWidth(View view1, View view2, int width)172 private boolean viewsCoverEntireWidth(View view1, View view2, int width) { 173 return view1.getLeft() <= 0 174 && view1.getRight() >= view2.getLeft() 175 && view2.getRight() >= width; 176 } 177 } 178