1 /*
2  * Copyright (C) 2008 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.widget.cts;
18 
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 
23 import android.Manifest;
24 import android.app.Activity;
25 import android.app.Instrumentation;
26 import android.util.AttributeSet;
27 import android.util.Xml;
28 import android.view.View;
29 import android.view.ViewConfiguration;
30 import android.widget.ListView;
31 import android.widget.ZoomButton;
32 
33 import androidx.test.InstrumentationRegistry;
34 import androidx.test.annotation.UiThreadTest;
35 import androidx.test.filters.LargeTest;
36 import androidx.test.filters.SmallTest;
37 import androidx.test.rule.ActivityTestRule;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
41 import com.android.compatibility.common.util.CtsTouchUtils;
42 
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.xmlpull.v1.XmlPullParser;
48 
49 import java.util.concurrent.CountDownLatch;
50 import java.util.concurrent.TimeUnit;
51 
52 @SmallTest
53 @RunWith(AndroidJUnit4.class)
54 public class ZoomButtonTest {
55     private static long NANOS_IN_MILLI = 1000000;
56 
57     private Instrumentation mInstrumentation;
58     private CtsTouchUtils mCtsTouchUtils;
59     private ZoomButton mZoomButton;
60     private Activity mActivity;
61 
62     @Rule(order = 0)
63     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
64             androidx.test.platform.app.InstrumentationRegistry
65                     .getInstrumentation().getUiAutomation(),
66             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
67 
68     @Rule(order = 1)
69     public ActivityTestRule<ZoomButtonCtsActivity> mActivityRule =
70             new ActivityTestRule<>(ZoomButtonCtsActivity.class);
71 
72     @Before
setup()73     public void setup() {
74         mInstrumentation = InstrumentationRegistry.getInstrumentation();
75         mCtsTouchUtils = new CtsTouchUtils(mInstrumentation.getTargetContext());
76         mActivity = mActivityRule.getActivity();
77         mZoomButton = (ZoomButton) mActivity.findViewById(R.id.zoombutton_test);
78     }
79 
80     @UiThreadTest
81     @Test
testConstructor()82     public void testConstructor() {
83         new ZoomButton(mActivity);
84 
85         new ZoomButton(mActivity, null);
86 
87         new ZoomButton(mActivity, null, android.R.attr.imageButtonStyle);
88 
89         new ZoomButton(mActivity, null, 0, android.R.style.Widget_Material_Light_ImageButton);
90 
91         XmlPullParser parser = mActivity.getResources().getXml(R.layout.zoombutton_layout);
92         AttributeSet attrs = Xml.asAttributeSet(parser);
93         assertNotNull(attrs);
94         new ZoomButton(mActivity, attrs);
95         new ZoomButton(mActivity, attrs, 0);
96     }
97 
98     @Test(expected=NullPointerException.class)
testConstructorWithNullContext1()99     public void testConstructorWithNullContext1() {
100         new ZoomButton(null);
101     }
102 
103     @Test(expected=NullPointerException.class)
testConstructorWithNullContext2()104     public void testConstructorWithNullContext2() {
105         new ZoomButton(null, null);
106     }
107 
108     @Test(expected=NullPointerException.class)
testConstructorWithNullContext3()109     public void testConstructorWithNullContext3() {
110         new ZoomButton(null, null, 0);
111     }
112 
113     @UiThreadTest
114     @Test
testSetEnabled()115     public void testSetEnabled() {
116         assertFalse(mZoomButton.isPressed());
117         mZoomButton.setEnabled(true);
118         assertTrue(mZoomButton.isEnabled());
119         assertFalse(mZoomButton.isPressed());
120 
121         mZoomButton.setPressed(true);
122         assertTrue(mZoomButton.isPressed());
123         mZoomButton.setEnabled(true);
124         assertTrue(mZoomButton.isEnabled());
125         assertTrue(mZoomButton.isPressed());
126 
127         mZoomButton.setEnabled(false);
128         assertFalse(mZoomButton.isEnabled());
129         assertFalse(mZoomButton.isPressed());
130     }
131 
132     @UiThreadTest
133     @Test
testDispatchUnhandledMove()134     public void testDispatchUnhandledMove() {
135         assertFalse(mZoomButton.dispatchUnhandledMove(new ListView(mActivity), View.FOCUS_DOWN));
136 
137         assertFalse(mZoomButton.dispatchUnhandledMove(null, View.FOCUS_DOWN));
138     }
139 
140     @LargeTest
141     @Test
testSetZoomSpeed()142     public void testSetZoomSpeed() throws Throwable {
143         final long[] zoomSpeeds = { 0, 100 };
144         mZoomButton.setEnabled(true);
145         ZoomClickListener zoomClickListener = new ZoomClickListener();
146         mZoomButton.setOnClickListener(zoomClickListener);
147         final ZoomLongClickListener longClickListener = new ZoomLongClickListener();
148         mZoomButton.setOnLongClickListener(longClickListener);
149 
150         for (long zoomSpeed : zoomSpeeds) {
151             // Reset the tracking state of our listener, but continue using it for testing
152             // various zoom speeds on the same ZoomButton
153             zoomClickListener.reset();
154 
155             mZoomButton.setZoomSpeed(zoomSpeed);
156 
157             final long startTime = System.nanoTime();
158             // Emulate long click
159             long longPressWait = ViewConfiguration.getLongPressTimeout() + zoomSpeed + 200;
160             mCtsTouchUtils.emulateLongPressOnViewCenter(mInstrumentation, mActivityRule,
161                     mZoomButton, longPressWait);
162 
163             final Long callbackFirstInvocationTime = zoomClickListener.getTimeOfFirstClick();
164             assertNotNull("Expecting at least one callback", callbackFirstInvocationTime);
165 
166             // Verify that the first callback is fired after the system-level long press timeout.
167             final long minTimeUntilFirstInvocationMs = ViewConfiguration.getLongPressTimeout();
168             final long actualTimeUntilFirstInvocationNs = callbackFirstInvocationTime - startTime;
169             assertTrue("First callback not during long press timeout was "
170                             + actualTimeUntilFirstInvocationNs / NANOS_IN_MILLI
171                             + " while long press timeout is " + minTimeUntilFirstInvocationMs,
172                     actualTimeUntilFirstInvocationNs
173                             > minTimeUntilFirstInvocationMs * NANOS_IN_MILLI);
174             assertTrue(longClickListener.postLatch.await(1, TimeUnit.SECONDS));
175             assertTrue("First callback should have happened sooner than "
176                             + actualTimeUntilFirstInvocationNs / NANOS_IN_MILLI,
177                     actualTimeUntilFirstInvocationNs
178                             <= longClickListener.postAfterLongClick);
179         }
180     }
181 
182     private static class ZoomLongClickListener implements View.OnLongClickListener, Runnable {
183         public long postAfterLongClick;
184         public CountDownLatch postLatch;
185 
186         @Override
onLongClick(View v)187         public boolean onLongClick(View v) {
188             postLatch = new CountDownLatch(1);
189             v.post(this);
190             return false;
191         }
192 
193         @Override
run()194         public void run() {
195             postAfterLongClick = System.nanoTime();
196             postLatch.countDown();
197         }
198     }
199 
200     private static class ZoomClickListener implements View.OnClickListener {
201         private Long mTimeOfFirstClick = null;
202 
reset()203         public void reset() {
204             mTimeOfFirstClick = null;
205         }
206 
getTimeOfFirstClick()207         public Long getTimeOfFirstClick() {
208             return mTimeOfFirstClick;
209         }
210 
onClick(View v)211         public void onClick(View v) {
212             if (mTimeOfFirstClick == null) {
213                 // Mark the current system time as the time of first click
214                 mTimeOfFirstClick = System.nanoTime();
215             }
216         }
217     }
218 }
219