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 package android.view.cts;
17 
18 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
19 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
20 
21 import android.annotation.SuppressLint;
22 import android.app.Activity;
23 import android.content.pm.ActivityInfo;
24 import android.content.pm.PackageManager;
25 import android.util.Log;
26 import android.view.AttachedSurfaceControl;
27 
28 import androidx.lifecycle.Lifecycle;
29 import androidx.test.core.app.ActivityScenario;
30 import androidx.test.filters.LargeTest;
31 import androidx.test.filters.RequiresDevice;
32 import androidx.test.platform.app.InstrumentationRegistry;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import org.junit.Assert;
36 import org.junit.Assume;
37 import org.junit.Before;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.util.concurrent.CountDownLatch;
42 import java.util.concurrent.TimeUnit;
43 import java.util.function.IntConsumer;
44 
45 @RunWith(AndroidJUnit4.class)
46 @LargeTest
47 @SuppressLint("RtlHardcoded")
48 @RequiresDevice
49 public class AttachedSurfaceControlTest {
50     private static final String TAG = "AttachedSurfaceControlTest";
51 
52     private static class TransformHintListener implements
53             AttachedSurfaceControl.OnBufferTransformHintChangedListener {
54         Activity activity;
55         int expectedOrientation;
56         CountDownLatch latch = new CountDownLatch(1);
57         IntConsumer hintConsumer;
58 
TransformHintListener(Activity activity, int expectedOrientation, IntConsumer hintConsumer)59         TransformHintListener(Activity activity, int expectedOrientation,
60                 IntConsumer hintConsumer) {
61             this.activity = activity;
62             this.expectedOrientation = expectedOrientation;
63             this.hintConsumer = hintConsumer;
64         }
65 
66         @Override
onBufferTransformHintChanged(int hint)67         public void onBufferTransformHintChanged(int hint) {
68             int orientation = activity.getResources().getConfiguration().orientation;
69             Log.d(TAG, "onBufferTransformHintChanged: orientation actual=" + orientation
70                     + " expected=" + expectedOrientation + " transformHint=" + hint);
71             Assert.assertEquals("Failed to switch orientation hint=" + hint, orientation,
72                     expectedOrientation);
73             hintConsumer.accept(hint);
74             latch.countDown();
75             activity.getWindow().getRootSurfaceControl()
76                     .removeOnBufferTransformHintChangedListener(this);
77         }
78     }
79 
80 
81     @Before
setup()82     public void setup() {
83         PackageManager pm =
84                 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
85         boolean supportsRotation = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)
86                 && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
87         Assume.assumeTrue(supportsRotation);
88     }
89 
90     @Test
testOnBufferTransformHintChangedListener()91     public void testOnBufferTransformHintChangedListener() throws InterruptedException {
92         final int[] transformHintResult = new int[2];
93         final CountDownLatch[] firstCallback = new CountDownLatch[1];
94         final CountDownLatch[] secondCallback = new CountDownLatch[1];
95         try (ActivityScenario<HandleConfigurationActivity> scenario =
96                      ActivityScenario.launch(HandleConfigurationActivity.class)) {
97             scenario.moveToState(Lifecycle.State.RESUMED);
98             scenario.onActivity(activity -> {
99                 int requestedOrientation = getRequestedOrientation(activity);
100                 TransformHintListener listener = new TransformHintListener(activity,
101                         requestedOrientation, hint -> transformHintResult[0] = hint);
102                 firstCallback[0] = listener.latch;
103                 activity.getWindow().getRootSurfaceControl()
104                         .addOnBufferTransformHintChangedListener(listener);
105                 setRequestedOrientation(activity, requestedOrientation);
106             });
107             // Check we get a callback since the orientation has changed and we expect transform
108             // hint to change.
109             Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS));
110 
111             // Check the callback value matches the call to get the transform hint.
112             scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0],
113                     activity.getWindow().getRootSurfaceControl().getBufferTransformHint()));
114 
115             scenario.onActivity(activity -> {
116                 int requestedOrientation = getRequestedOrientation(activity);
117                 TransformHintListener listener = new TransformHintListener(activity,
118                         requestedOrientation, hint -> transformHintResult[1] = hint);
119                 secondCallback[0] = listener.latch;
120                 activity.getWindow().getRootSurfaceControl()
121                         .addOnBufferTransformHintChangedListener(listener);
122                 setRequestedOrientation(activity, requestedOrientation);
123             });
124             // Check we get a callback since the orientation has changed and we expect transform
125             // hint to change.
126             Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS));
127 
128             // Check the callback value matches the call to get the transform hint.
129             scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1],
130                     activity.getWindow().getRootSurfaceControl().getBufferTransformHint()));
131         }
132 
133         // If the app orientation was changed, we should get a different transform hint
134         Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]);
135     }
136 
getRequestedOrientation(Activity activity)137     private int getRequestedOrientation(Activity activity) {
138         int currentOrientation = activity.getResources().getConfiguration().orientation;
139         return currentOrientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
140                 : ORIENTATION_LANDSCAPE;
141     }
142 
setRequestedOrientation(Activity activity, int requestedOrientation)143     private void setRequestedOrientation(Activity activity,
144             /* @Configuration.Orientation */ int requestedOrientation) {
145         /* @ActivityInfo.ScreenOrientation */
146         Log.d(TAG, "setRequestedOrientation: requestedOrientation=" + requestedOrientation);
147         int screenOrientation =
148                 requestedOrientation == ORIENTATION_LANDSCAPE
149                         ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
150                         : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
151         activity.setRequestedOrientation(screenOrientation);
152     }
153 
154     @Test
testOnBufferTransformHintChangesFromLandToSea()155     public void testOnBufferTransformHintChangesFromLandToSea() throws InterruptedException {
156         final int[] transformHintResult = new int[2];
157         final CountDownLatch[] firstCallback = new CountDownLatch[1];
158         final CountDownLatch[] secondCallback = new CountDownLatch[1];
159         try (ActivityScenario<HandleConfigurationActivity> scenario =
160                      ActivityScenario.launch(HandleConfigurationActivity.class)) {
161             scenario.moveToState(Lifecycle.State.RESUMED);
162             scenario.onActivity(activity -> {
163                 if (activity.getResources().getConfiguration().orientation
164                         == ORIENTATION_LANDSCAPE) {
165                     return;
166                 }
167                 TransformHintListener listener = new TransformHintListener(activity,
168                         ORIENTATION_LANDSCAPE, hint -> transformHintResult[0] = hint);
169                 firstCallback[0] = listener.latch;
170                 activity.getWindow().getRootSurfaceControl()
171                         .addOnBufferTransformHintChangedListener(listener);
172                 setRequestedOrientation(activity, ORIENTATION_LANDSCAPE);
173             });
174 
175             // If the device is already in landscape, do nothing.
176             if (firstCallback[0] != null) {
177                 Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS));
178             }
179             // Check the callback value matches the call to get the transform hint.
180             scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0],
181                     activity.getWindow().getRootSurfaceControl().getBufferTransformHint()));
182 
183             scenario.onActivity(activity -> {
184                 TransformHintListener listener = new TransformHintListener(activity,
185                         ORIENTATION_LANDSCAPE, hint -> transformHintResult[1] = hint);
186                 secondCallback[0] = listener.latch;
187                 activity.getWindow().getRootSurfaceControl()
188                         .addOnBufferTransformHintChangedListener(listener);
189                 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
190             });
191             // Check we get a callback since the orientation has changed and we expect transform
192             // hint to change.
193             Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS));
194 
195             // Check the callback value matches the call to get the transform hint.
196             scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1],
197                     activity.getWindow().getRootSurfaceControl().getBufferTransformHint()));
198         }
199 
200         // If the app orientation was changed, we should get a different transform hint
201         Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]);
202     }
203 
204 }
205