1 /* 2 * Copyright (C) 2016 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 android.view.cts; 18 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.Mockito.any; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.verify; 26 27 import android.app.Activity; 28 import android.app.Instrumentation; 29 import android.graphics.Color; 30 import android.graphics.Rect; 31 import android.graphics.drawable.ColorDrawable; 32 import android.graphics.drawable.Drawable; 33 import android.platform.test.annotations.Presubmit; 34 import android.support.test.InstrumentationRegistry; 35 import android.support.test.annotation.UiThreadTest; 36 import android.support.test.filters.MediumTest; 37 import android.support.test.rule.ActivityTestRule; 38 import android.support.test.runner.AndroidJUnit4; 39 import android.util.Pair; 40 import android.view.View; 41 import android.view.ViewGroup; 42 import android.view.ViewGroupOverlay; 43 import android.view.cts.util.DrawingUtils; 44 45 import com.android.compatibility.common.util.CtsTouchUtils; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 52 import java.util.ArrayList; 53 import java.util.List; 54 55 @MediumTest 56 @RunWith(AndroidJUnit4.class) 57 public class ViewGroupOverlayTest { 58 private Instrumentation mInstrumentation; 59 private Activity mActivity; 60 private ViewGroup mViewGroupWithOverlay; 61 private ViewGroupOverlay mViewGroupOverlay; 62 63 @Rule 64 public ActivityTestRule<ViewGroupOverlayCtsActivity> mActivityRule = 65 new ActivityTestRule<>(ViewGroupOverlayCtsActivity.class); 66 67 @Before 68 public void setup() { 69 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 70 mActivity = mActivityRule.getActivity(); 71 mViewGroupWithOverlay = (ViewGroup) mActivity.findViewById(R.id.viewgroup_with_overlay); 72 mViewGroupOverlay = mViewGroupWithOverlay.getOverlay(); 73 } 74 75 @Presubmit 76 @Test 77 public void testBasics() { 78 DrawingUtils.assertAllPixelsOfColor("Default fill", mViewGroupWithOverlay, 79 Color.WHITE, null); 80 assertNotNull("Overlay is not null", mViewGroupOverlay); 81 } 82 83 @UiThreadTest 84 @Test(expected=IllegalArgumentException.class) 85 public void testAddNullView() { 86 mViewGroupOverlay.add((View) null); 87 } 88 89 @UiThreadTest 90 @Test(expected=IllegalArgumentException.class) 91 public void testRemoveNullView() { 92 mViewGroupOverlay.remove((View) null); 93 } 94 95 @UiThreadTest 96 @Test 97 public void testOverlayWithOneView() { 98 // Add one colored view to the overlay 99 final View redView = new View(mActivity); 100 redView.setBackgroundColor(Color.RED); 101 redView.layout(10, 20, 30, 40); 102 103 mViewGroupOverlay.add(redView); 104 105 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 106 colorRectangles.add(new Pair<>(new Rect(10, 20, 30, 40), Color.RED)); 107 DrawingUtils.assertAllPixelsOfColor("Overlay with one red view", 108 mViewGroupWithOverlay, Color.WHITE, colorRectangles); 109 110 // Now remove that view from the overlay and test that we're back to pure white fill 111 mViewGroupOverlay.remove(redView); 112 DrawingUtils.assertAllPixelsOfColor("Back to default fill", mViewGroupWithOverlay, 113 Color.WHITE, null); 114 } 115 116 @UiThreadTest 117 @Test 118 public void testOverlayWithNonOverlappingViews() { 119 // Add three views to the overlay 120 final View redView = new View(mActivity); 121 redView.setBackgroundColor(Color.RED); 122 redView.layout(10, 20, 30, 40); 123 final View greenView = new View(mActivity); 124 greenView.setBackgroundColor(Color.GREEN); 125 greenView.layout(60, 30, 90, 50); 126 final View blueView = new View(mActivity); 127 blueView.setBackgroundColor(Color.BLUE); 128 blueView.layout(40, 60, 80, 90); 129 130 mViewGroupOverlay.add(redView); 131 mViewGroupOverlay.add(greenView); 132 mViewGroupOverlay.add(blueView); 133 134 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 135 colorRectangles.add(new Pair<>(new Rect(10, 20, 30, 40), Color.RED)); 136 colorRectangles.add(new Pair<>(new Rect(60, 30, 90, 50), Color.GREEN)); 137 colorRectangles.add(new Pair<>(new Rect(40, 60, 80, 90), Color.BLUE)); 138 DrawingUtils.assertAllPixelsOfColor("Overlay with three views", mViewGroupWithOverlay, 139 Color.WHITE, colorRectangles); 140 141 // Remove one of the views from the overlay 142 mViewGroupOverlay.remove(greenView); 143 colorRectangles.clear(); 144 colorRectangles.add(new Pair<>(new Rect(10, 20, 30, 40), Color.RED)); 145 colorRectangles.add(new Pair<>(new Rect(40, 60, 80, 90), Color.BLUE)); 146 DrawingUtils.assertAllPixelsOfColor("Overlay with two views", mViewGroupWithOverlay, 147 Color.WHITE, colorRectangles); 148 149 // Clear all views from the overlay and test that we're back to pure white fill 150 mViewGroupOverlay.clear(); 151 DrawingUtils.assertAllPixelsOfColor("Back to default fill", mViewGroupWithOverlay, 152 Color.WHITE, null); 153 } 154 155 @UiThreadTest 156 @Test 157 public void testOverlayWithNonOverlappingViewAndDrawable() { 158 // Add one view and one drawable to the overlay 159 final View redView = new View(mActivity); 160 redView.setBackgroundColor(Color.RED); 161 redView.layout(10, 20, 30, 40); 162 final Drawable greenDrawable = new ColorDrawable(Color.GREEN); 163 greenDrawable.setBounds(60, 30, 90, 50); 164 165 mViewGroupOverlay.add(redView); 166 mViewGroupOverlay.add(greenDrawable); 167 168 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 169 colorRectangles.add(new Pair<>(new Rect(10, 20, 30, 40), Color.RED)); 170 colorRectangles.add(new Pair<>(new Rect(60, 30, 90, 50), Color.GREEN)); 171 DrawingUtils.assertAllPixelsOfColor("Overlay with one view and one drawable", 172 mViewGroupWithOverlay, Color.WHITE, colorRectangles); 173 174 // Remove the view from the overlay 175 mViewGroupOverlay.remove(redView); 176 colorRectangles.clear(); 177 colorRectangles.add(new Pair<>(new Rect(60, 30, 90, 50), Color.GREEN)); 178 DrawingUtils.assertAllPixelsOfColor("Overlay with one drawable", mViewGroupWithOverlay, 179 Color.WHITE, colorRectangles); 180 181 // Clear everything from the overlay and test that we're back to pure white fill 182 mViewGroupOverlay.clear(); 183 DrawingUtils.assertAllPixelsOfColor("Back to default fill", mViewGroupWithOverlay, 184 Color.WHITE, null); 185 } 186 187 @UiThreadTest 188 @Test 189 public void testOverlayWithOverlappingViews() { 190 // Add two overlapping colored views to the overlay 191 final View redView = new View(mActivity); 192 redView.setBackgroundColor(Color.RED); 193 redView.layout(10, 20, 60, 40); 194 final View greenView = new View(mActivity); 195 greenView.setBackgroundColor(Color.GREEN); 196 greenView.layout(30, 20, 80, 40); 197 198 mViewGroupOverlay.add(redView); 199 mViewGroupOverlay.add(greenView); 200 201 // Our overlay views overlap in horizontal 30-60 range. Here we test that the 202 // second view is the one that is drawn last in that range. 203 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 204 colorRectangles.add(new Pair<>(new Rect(10, 20, 30, 40), Color.RED)); 205 colorRectangles.add(new Pair<>(new Rect(30, 20, 80, 40), Color.GREEN)); 206 DrawingUtils.assertAllPixelsOfColor("Overlay with two drawables", mViewGroupWithOverlay, 207 Color.WHITE, colorRectangles); 208 209 // Remove the second view from the overlay 210 mViewGroupOverlay.remove(greenView); 211 colorRectangles.clear(); 212 colorRectangles.add(new Pair<>(new Rect(10, 20, 60, 40), Color.RED)); 213 DrawingUtils.assertAllPixelsOfColor("Overlay with one drawable", mViewGroupWithOverlay, 214 Color.WHITE, colorRectangles); 215 216 // Clear all views from the overlay and test that we're back to pure white fill 217 mViewGroupOverlay.clear(); 218 DrawingUtils.assertAllPixelsOfColor("Back to default fill", mViewGroupWithOverlay, 219 Color.WHITE, null); 220 } 221 222 @UiThreadTest 223 @Test 224 public void testOverlayWithOverlappingViewAndDrawable() { 225 // Add two overlapping colored views to the overlay 226 final Drawable redDrawable = new ColorDrawable(Color.RED); 227 redDrawable.setBounds(10, 20, 60, 40); 228 final View greenView = new View(mActivity); 229 greenView.setBackgroundColor(Color.GREEN); 230 greenView.layout(30, 20, 80, 40); 231 232 mViewGroupOverlay.add(redDrawable); 233 mViewGroupOverlay.add(greenView); 234 235 // Our overlay views overlap in horizontal 30-60 range. Even though the green view was 236 // added after the red drawable, *all* overlay drawables are drawn after the overlay views. 237 // So in the overlap range we expect color red 238 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 239 colorRectangles.add(new Pair<>(new Rect(10, 20, 60, 40), Color.RED)); 240 colorRectangles.add(new Pair<>(new Rect(60, 20, 80, 40), Color.GREEN)); 241 DrawingUtils.assertAllPixelsOfColor("Overlay with one view and one drawable", 242 mViewGroupWithOverlay, Color.WHITE, colorRectangles); 243 244 // Remove the drawable from the overlay 245 mViewGroupOverlay.remove(redDrawable); 246 colorRectangles.clear(); 247 colorRectangles.add(new Pair<>(new Rect(30, 20, 80, 40), Color.GREEN)); 248 DrawingUtils.assertAllPixelsOfColor("Overlay with one view", mViewGroupWithOverlay, 249 Color.WHITE, colorRectangles); 250 251 // Clear all views from the overlay and test that we're back to pure white fill 252 mViewGroupOverlay.clear(); 253 DrawingUtils.assertAllPixelsOfColor("Back to default fill", mViewGroupWithOverlay, 254 Color.WHITE, null); 255 } 256 257 @Test 258 public void testOverlayViewNoClicks() throws Throwable { 259 // Add one colored view with mock click listener to the overlay 260 final View redView = new View(mActivity); 261 redView.setBackgroundColor(Color.RED); 262 final View.OnClickListener mockClickListener = mock(View.OnClickListener.class); 263 redView.setOnClickListener(mockClickListener); 264 redView.layout(10, 20, 30, 40); 265 266 mActivityRule.runOnUiThread(() -> mViewGroupOverlay.add(redView)); 267 268 // Emulate a tap in the center of the view we've added to the overlay 269 CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mViewGroupWithOverlay); 270 271 // Verify that our mock listener hasn't been called 272 verify(mockClickListener, never()).onClick(any(View.class)); 273 } 274 275 @UiThreadTest 276 @Test 277 public void testOverlayReparenting() { 278 // Find the view that we're about to add to our overlay 279 final View level2View = mViewGroupWithOverlay.findViewById(R.id.level2); 280 final View level3View = level2View.findViewById(R.id.level3); 281 282 assertTrue(level2View == level3View.getParent()); 283 284 // Set the fill of this view to red 285 level3View.setBackgroundColor(Color.RED); 286 mViewGroupOverlay.add(level3View); 287 288 // At this point we expect the view that was added to the overlay to have been removed 289 // from its original parent 290 assertFalse(level2View == level3View.getParent()); 291 292 // Check the expected visual appearance of our view group. We expect that the view that 293 // was added to the overlay to maintain its relative location inside the activity. 294 final List<Pair<Rect, Integer>> colorRectangles = new ArrayList<>(); 295 colorRectangles.add(new Pair<>(new Rect(65, 60, 85, 90), Color.RED)); 296 DrawingUtils.assertAllPixelsOfColor("Empty overlay before adding grandchild", 297 mViewGroupWithOverlay, Color.WHITE, colorRectangles); 298 299 } 300 } 301