1 /* 2 * Copyright (C) 2021 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.server.wm.jetpack.utils; 18 19 import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.graphics.Rect; 25 import android.os.Bundle; 26 import android.view.View; 27 28 import androidx.annotation.Nullable; 29 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.TimeUnit; 32 33 /** 34 * Test activity that can verify whether the layout changes. Copied from 35 * androidx.window.TestActivity. 36 */ 37 public class TestActivity extends Activity implements View.OnLayoutChangeListener { 38 39 private int mRootViewId; 40 private CountDownLatch mLayoutLatch; 41 private CountDownLatch mBoundsChangeLatch; 42 private final Rect mPreviousBounds = new Rect(); 43 private static CountDownLatch sResumeLatch = new CountDownLatch(1); 44 45 @Override onCreate(@ullable Bundle savedInstanceState)46 public void onCreate(@Nullable Bundle savedInstanceState) { 47 super.onCreate(savedInstanceState); 48 final View contentView = new TestContentViewForConfigurationChange(this); 49 mRootViewId = View.generateViewId(); 50 contentView.setId(mRootViewId); 51 setContentView(contentView); 52 53 resetLayoutCounter(); 54 resetBoundsChangeCounter(); 55 getWindow().getDecorView().addOnLayoutChangeListener(this); 56 } 57 getWidth()58 int getWidth() { 59 return findViewById(mRootViewId).getWidth(); 60 } 61 getHeight()62 int getHeight() { 63 return findViewById(mRootViewId).getHeight(); 64 } 65 66 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)67 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 68 int oldTop, int oldRight, int oldBottom) { 69 mLayoutLatch.countDown(); 70 } 71 72 @Override onResume()73 protected void onResume() { 74 super.onResume(); 75 sResumeLatch.countDown(); 76 } 77 78 /** 79 * Resets layout counter when waiting for a layout to happen before calling 80 * {@link #waitForLayout()}. 81 */ resetLayoutCounter()82 public void resetLayoutCounter() { 83 mLayoutLatch = new CountDownLatch(1); 84 } 85 86 /** 87 * Resets bounds change counter when waiting for a bounds change to happen before calling 88 * {@link #waitForBoundsChange()}. 89 */ resetBoundsChangeCounter()90 public void resetBoundsChangeCounter() { 91 mPreviousBounds.set(getActivityBounds(this)); 92 mBoundsChangeLatch = new CountDownLatch(1); 93 } 94 95 /** 96 * Blocks and waits for the next layout to happen. {@link #resetLayoutCounter()} must be called 97 * before calling this method. 98 * @return {@code true} if the layout happened before the timeout count reached zero and 99 * {@code false} if the waiting time elapsed before the layout happened. 100 */ waitForLayout()101 public boolean waitForLayout() { 102 try { 103 return mLayoutLatch.await(3, TimeUnit.SECONDS); 104 } catch (InterruptedException e) { 105 return false; 106 } 107 } 108 109 /** 110 * Blocks and waits for the next bounds change to happen. {@link #resetBoundsChangeCounter()} 111 * must be called before calling this method. 112 * @return {@code true} if the bounds change happened before the timeout count reached zero and 113 * {@code false} if the waiting time elapsed before the bounds change happened. 114 */ waitForBoundsChange()115 public boolean waitForBoundsChange() { 116 try { 117 return mBoundsChangeLatch.await(3, TimeUnit.SECONDS); 118 } catch (InterruptedException e) { 119 return false; 120 } 121 } 122 123 /** 124 * Resets layout counter when waiting for a layout to happen before calling 125 * {@link #waitForOnResume()}. 126 */ resetResumeCounter()127 public static void resetResumeCounter() { 128 sResumeLatch = new CountDownLatch(1); 129 } 130 131 /** 132 * Same as {@link #waitForLayout()}, but waits for onResume() to be called for any activity of 133 * this class. This can be used to track activity re-creation. 134 * @return {@code true} if the onResume() happened before the timeout count reached zero and 135 * {@code false} if the waiting time elapsed before the onResume() happened. 136 */ waitForOnResume()137 public static boolean waitForOnResume() { 138 try { 139 return sResumeLatch.await(3, TimeUnit.SECONDS); 140 } catch (InterruptedException e) { 141 return false; 142 } 143 } 144 145 /** 146 * Sometimes activity configuration change does not trigger when embedding status change, need 147 * to use View's instead. 148 */ 149 class TestContentViewForConfigurationChange extends View { TestContentViewForConfigurationChange(Context context)150 public TestContentViewForConfigurationChange(Context context) { 151 super(context); 152 } 153 154 @Override onConfigurationChanged(Configuration newConfig)155 protected void onConfigurationChanged(Configuration newConfig) { 156 super.onConfigurationChanged(newConfig); 157 final Rect newActivityBounds = getActivityBounds(TestActivity.this); 158 if (!newActivityBounds.equals(mPreviousBounds)) { 159 mPreviousBounds.set(newActivityBounds); 160 mBoundsChangeLatch.countDown(); 161 } 162 } 163 } 164 } 165